Telegram setMyCommands Not Called on Gateway Restart in OpenClaw 2026.4.14
After upgrading to OpenClaw 2026.4.14, Telegram bot slash commands disappear on gateway restart due to the registerTelegramNativeCommands initialization being skipped, resulting in an empty command menu.
๐ Symptoms
- Primary Symptom: All Telegram bot slash commands (42โ58 commands) disappear from the menu after a gateway restart.
- API Verification: The
getMyCommandsTelegram Bot API call returns an empty list:curl -s "https://api.telegram.org/bot<TOKEN>/getMyCommands" {"ok":true,"result":[]} - Log Absence: The gateway startup logs omit the expected command registration message:
{"subsystem":"gateway/channels/telegram"} Telegram menu text exceeded the conservative 5700-character payload budget; shortening descriptions to keep 58 commands visible. - Behavioral Inconsistency: Earlier restarts in the same session did successfully register commands, while later restarts skip registration entirely. This suggests a non-deterministic initialization failure.
- Workaround Confirmation: Manually invoking
setMyCommandsvia the Bot API successfully restores commands, confirming the Telegram API itself is functional and the issue lies in the OpenClaw initialization sequence.
Diagnostic Command for Symptom Confirmation
# Check if commands are registered (should return non-empty array after successful init)
curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMyCommands" | jq '.result | length'
# Expected output: > 0 if commands are registered
# Symptomatic output: 0๐ง Root Cause
Technical Analysis
The root cause lies in a race condition or initialization order dependency introduced in OpenClaw 2026.4.14 (commit 323493f). The registerTelegramNativeCommands function, located in the compiled bundle bot-BwMz6R6-.js, is not being invoked during the gateway startup sequence.
Failure Sequence
- Gateway Startup: The OpenClaw gateway initializes all configured channels, including the Telegram provider (grammy-based).
- Provider Initialization: The Telegram provider emits a
readyevent or equivalent lifecycle signal upon successful connection to the Telegram Bot API. - Command Registration Trigger: The
registerTelegramNativeCommandsfunction should be called upon provider readiness to register the slash command menu. - Failure Point: In 2026.4.14, the registration trigger is either:
- Attached to an event that fires before the command definitions are fully loaded
- Subject to a conditional check that evaluates to
falsedue to timing - Blocked by an unhandled promise rejection that silently fails
Architectural Inconsistency
The inconsistent behavior (some restarts work, others fail) indicates a time-of-day independent race condition. Possible contributing factors:
- Async Initialization Chain: If command registration depends on external data fetching (e.g., loading command definitions from a database or config store), network latency variations can cause the registration to execute before data is available.
- Event Handler Attachment Timing: The
readyevent handler may be attached after the event has already fired on fast-starting instances. - Module Import Order: ES module initialization order differences between hot-restarts and cold-starts.
- Connection Pool State: In long-running gateway sessions, accumulated connection pool state may affect initialization timing.
Affected Code Path
// Simplified representation of the affected initialization path
async function initializeTelegramProvider(config) {
const provider = new GrammyTelegramProvider(config);
// This handler attachment may race with the ready event
provider.on('ready', async () => {
// This block may not execute if 'ready' already fired
await registerTelegramNativeCommands(provider, config.commands);
});
await provider.start(); // ready event fires here on fast systems
}๐ ๏ธ Step-by-Step Fix
Immediate Workarounds
Option 1: Manual Command Registration (Temporary Fix)
# Using curl to set commands directly via Telegram Bot API
curl -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setMyCommands" \
-H "Content-Type: application/json" \
-d '{
"commands": [
{"command": "help", "description": "Show help information"},
{"command": "status", "description": "Check system status"},
{"command": "model", "description": "Switch AI model"},
{"command": "cancel", "description": "Cancel ongoing operation"}
]
}'
# Verify the response contains {"ok": true, ...}Option 2: Cron Job as Band-Aid
# Add to crontab (crontab -e)
# Restart gateway and immediately re-register commands
@reboot sleep 30 && /opt/openclaw/scripts/reregister-commands.shOption 3: Force Re-registration via OpenClaw CLI
# If OpenClaw provides a CLI tool
openclaw commands register --channel telegram --force
# Or via the management API
curl -X POST "http://localhost:3000/api/channels/telegram/commands/reload" \
-H "Authorization: Bearer ${MANAGEMENT_API_TOKEN}"Definitive Fix: Version Remediation
Step 1: Identify Current Version
cat /opt/openclaw/package.json | grep '"version"'
# Or via CLI
openclaw --versionStep 2: Rollback to Previous Stable Version
# If using npm
npm install [email protected] --save
# If using Docker
# Edit docker-compose.yml
image: openclaw/openclaw:2026.4.13
# Rebuild and restart
docker-compose down && docker-compose up -d --buildStep 3: Upgrade to Fixed Version (When Available)
# Monitor the OpenClaw GitHub releases for 2026.4.15 or later
# Once available:
npm install openclaw@latest --save
# or
docker-compose pull && docker-compose up -dHot-Fix: Patch the Initialization Order
Warning: This is a temporary manual fix; apply during maintenance window.
# Locate the compiled bundle
find /opt/openclaw -name "bot-BwMz6R6-.js" 2>/dev/null
# Backup original
cp /opt/openclaw/dist/bot-BwMz6R6-.js /opt/openclaw/dist/bot-BwMz6R6-.js.bak
# Add synchronous registration call after provider initialization
# Insert before the existing ready handler:
#
# BEFORE (line ~XXX):
# provider.on('ready', async () => {
#
# AFTER (patch):
# // Immediate registration attempt
# registerTelegramNativeCommands(provider, config.commands).catch(console.error);
# provider.on('ready', async () => {
# await registerTelegramNativeCommands(provider, config.commands);
# });๐งช Verification
Post-Fix Verification Steps
- Restart the Gateway
# For systemd-managed installations
sudo systemctl restart openclaw-gateway
# For Docker
docker-compose restart gateway
# For PM2
pm2 restart openclaw- Check Gateway Logs for Command Registration
# Check for the expected log message
sudo journalctl -u openclaw-gateway -f | grep -i "setMyCommands\|menu text exceeded\|Telegram menu"
# Expected output (success):
# {"subsystem":"gateway/channels/telegram"} Telegram menu text exceeded the conservative 5700-character payload budget; shortening descriptions to keep 58 commands visible.- Verify via Telegram Bot API
# Get the number of registered commands
COMMAND_COUNT=$(curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMyCommands" | jq '.result | length')
echo "Registered commands: ${COMMAND_COUNT}"
# Expected: > 0 (e.g., 42, 58, etc.)
# Failure: 0
# Full API response for debugging
curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMyCommands" | jq .- Test in Telegram Client
# Open Telegram and check the bot's / menu
# All previously available slash commands should appear
# Test a known command (e.g., /help) to confirm functionality- Perform Multiple Consecutive Restarts
# Verify consistency across restarts (run 3 times)
for i in 1 2 3; do
echo "=== Restart $i ==="
sudo systemctl restart openclaw-gateway
sleep 10
COUNT=$(curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMyCommands" | jq '.result | length')
echo "Command count: $COUNT"
if [ "$COUNT" -eq 0 ]; then
echo "FAILURE: Commands not registered on restart $i"
exit 1
fi
done
echo "SUCCESS: Commands registered consistently across all restarts"Verification Exit Criteria
- โ Gateway logs contain the "Telegram menu text exceeded" message on startup
- โ
getMyCommandsreturns a non-empty array - โ
All expected commands appear in the Telegram bot's
/menu - โ Commands persist after multiple consecutive restarts
- โ Exit code 0 from the consecutive restart test script
โ ๏ธ Common Pitfalls
- Assuming All Restarts Behave Identically: The bug manifests inconsistently. A few successful restarts do not guarantee the issue is resolved. Always verify using the consecutive restart test.
- Ignoring Log Filtering: The relevant log message may be truncated or split across multiple lines depending on your logging configuration. Use
grep -i telegramwithout additional filters to ensure you capture all related entries. - Docker Layer Caching: If using Docker, ensure you force a rebuild after version changes:
docker-compose build --no-cache docker-compose up -d - Environment Variable Timing: The
TELEGRAM_BOT_TOKENenvironment variable must be set before the gateway starts. Check that your environment file is correctly loaded:# Verify environment is loaded env | grep TELEGRAM - Node.js Version Interactions: Node 22.22.1 may exhibit different async timing than other versions. Test with the same Node version that was used in the working environment (pre-upgrade).
- Gateway High Availability Configurations: In multi-instance setups, ensure command registration is idempotent. Duplicate
setMyCommandscalls may trigger Telegram's rate limiting. - Missing Graceful Shutdown: Abruptly killing the gateway process may leave the Telegram Webhook connection in an inconsistent state. Always use graceful shutdown signals:
kill -SIGTERM <pid> # Preferred # Avoid: kill -SIGKILL <pid> - Command Description Length: If you've added custom commands with descriptions exceeding 256 characters, the Telegram API will reject the entire batch. The "menu text exceeded" log message indicates this safeguard is active.
- Firewall/Proxy Interference: Corporate proxies may delay the Telegram API response, exacerbating the race condition. Test direct connectivity:
curl -v --max-time 10 "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMe"
๐ Related Errors
UNAUTHORIZED(401) on Bot API calls โ Typically indicates incorrect bot token, not this issue. Verify token configuration if encountered.Too Many Requests(429) from Telegram API โ Rate limiting during repeated command registration attempts. Implement exponential backoff if implementing automated re-registration.- Empty
getMyCommandsresponse post-upgrade (Issue #4521) โ This is the primary symptom of the bug documented in this guide. - Gateway fails to connect to Telegram with
ETIMEDOUTโ Network connectivity issue, unrelated to the command registration bug but may appear concurrently. - Commands disappear after bot token rotation โ Expected behavior when using a new token; commands are token-specific. Re-register after token rotation.
- Webhook vs Polling mode inconsistency โ If switching between Telegram connection modes, command registration state may not persist correctly across the transition.
- Historical:
setMyCommandspayload size error in 2026.3.x โ Previous version had a related bug where 60+ commands exceeded the conservative payload budget. This was partially addressed in 2026.4.14 with the 5700-character limit, but introduced the initialization regression.
Cross-Reference
- OpenClaw GitHub: Search issues tagged with
telegram+commandsfor ongoing patches - grammy documentation: https://grammy.dev/plugins/commands โ Bot command menu registration API details
- Telegram Bot API: setMyCommands โ Official API specification for command registration