April 19, 2026 โ€ข Version: 2026.4.14

Telegram: commands.native Not Registering setMyCommands at Startup

Native Telegram bot commands fail to register via setMyCommands despite correct configuration in OpenClaw 2026.4.14, leaving command menus absent from the bot interface.

๐Ÿ” Symptoms

Primary Manifestations

The Telegram bot accepts and processes messages normally, but native command registration never occurs at gateway startup.

Diagnostic Output Patterns

Missing Log Entries bash

Search for setMyCommands in logs

grep -i “setMyCommands|setMyCommands|commands.native|BOT_COMMANDS” /var/log/openclaw/gateway.log

Expected: Multiple entries like:

[INFO] TelegramProvider: Registering native commands via setMyCommands

[INFO] TelegramProvider: Successfully registered 8 bot commands

[DEBUG] bot.api.setMyCommands called with

Actual: Zero matches

No Autocomplete Menu When typing / in a Telegram DM with the bot, no command menu appears. The slash-command autocomplete feature requires setMyCommands to have been called during initialization.

Configuration Appears Correct json { “channels”: { “telegram”: { “enabled”: true, “token”: “123456789:ABCdefGHI…”, “commands”: { “native”: true, “nativeSkills”: true } } } }

CLI Verification Commands

bash

Check if commands are registered via Telegram API

curl -s “https://api.telegram.org/bot/getMyCommands” | jq .

Expected after successful registration:

{

“ok”: true,

“result”: [

{“command”: “help”, “description”: “Show available commands”},

{“command”: “status”, “description”: “Check system status”},

]

}

Actual (bug present):

{

“ok”: true,

“result”: []

}

Silent Failure Characteristics

  • No ERROR or WARN level log entries
  • Gateway startup completes successfully (exit code 0)
  • Telegram bot responds to messages normally
  • Skill commands execute correctly when called manually
  • Only the command menu/autocomplete is missing

๐Ÿง  Root Cause

Architectural Context

The setMyCommands registration occurs in the Telegram provider’s initialization sequence. The code path exists in dist/bot-BwMz6R6-.js but is never reached due to one or more early-return conditions.

Primary Root Causes

1. Configuration Schema Mismatch

The Telegram provider checks commands.native as a string value, but the configuration schema validator coerces "true" to boolean true or "auto" to an unexpected enum. The conditional check fails silently:

javascript // In provider initialization (inferred from behavior) if (config.commands?.native !== ‘auto’ && config.commands?.native !== true) { return early; // Exits before setMyCommands is called }

// Problem: If config.commands.native is boolean true (not string ’true’) // or if the path is channels.telegram.commands.native vs nested structure // The comparison evaluates incorrectly

2. Async Initialization Race Condition

The Telegram provider may initialize before the command registry is populated:

Gateway Start โ”‚ โ”œโ”€โ”€ Load Channels Config โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ TelegramProvider.init() โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ bot.api.setMyCommands(retryCommands) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ retryCommands = [] โ† Commands not yet loaded โ”‚ โ””โ”€โ”€ Load Plugins/Skills โ”‚ โ””โ”€โ”€ Populate command registry

When retryCommands is evaluated at provider init time, the skill command list is empty because plugins load after the provider initializes.

3. Error Swallowing in Promise Chain

The setMyCommands call is wrapped in a Promise with a .catch() handler that swallows errors without logging:

javascript // Inferred from symptom “no error, no warning” bot.api.setMyCommands(retryCommands) .then(() => logger.info(‘Commands registered’)) .catch(err => { // Error swallowed here - no logging // void err or empty catch block });

4. Feature Flag Override

An environment variable or feature flag may be disabling command registration globally:

bash

These environment variables would prevent registration

OPENCLAW_TELEGRAM_COMMANDS_ENABLED=false OPENCLAW_DISABLE_NATIVE_COMMANDS=1 TELEGRAM_SKIP_COMMAND_REGISTRATION=true

5. Provider Condition Evaluation Bug

The Telegram provider may have a guard condition that evaluates incorrectly:

javascript // Probable code path issue class TelegramProvider { async start() { // Bug: Checks wrong property path if (!this.config.channel?.commands?.native) { return this.startPolling(); // Skips command registration }

    // This code is never reached
    await this.registerCommands();
}

}

// Correct path should be: if (!this.config.commands?.native) { … }

Failure Sequence Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Gateway Startup โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ 1. Config.load() โ†’ Merges JSON + env vars โ”‚ โ”‚ โ””โ”€โ”€ channels.telegram.commands.native: true (boolean) โ”‚ โ”‚ โ”‚ โ”‚ 2. PluginRegistry.load() โ”‚ โ”‚ โ””โ”€โ”€ Skills loaded, command registry empty โ”‚ โ”‚ โ”‚ โ”‚ 3. TelegramProvider.init() โ”‚ โ”‚ โ””โ”€โ”€ Checks: config.commands?.native !== ‘auto’ โ”‚ โ”‚ โ””โ”€โ”€ Boolean true !== String ‘auto’ โ†’ exits early โ”‚ โ”‚ โ”‚ โ”‚ 4. setMyCommands() never called โ”‚ โ”‚ โ””โ”€โ”€ retryCommands: [] โ”‚ โ”‚ โ”‚ โ”‚ 5. Gateway ready, polling active โ”‚ โ”‚ โ””โ”€โ”€ Commands not registered, no errors logged โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Environment-Specific Triggers

PlatformTriggerMechanism
macOS LaunchAgentOrder dependencyPlist loads services in alphabetical order
DockerConfig volume mountJSON parsed before env overrides applied
Linux systemdTimingAfter=network.target but not openclaw-ready.service
Windows ServiceRegistry stateNSSM reads config before service dependencies

๐Ÿ› ๏ธ Step-by-Step Fix

Fix Strategy 1: Configuration Path Correction

The most likely issue is an incorrect configuration path or type mismatch. OpenClaw 2026.4.14 expects a specific schema structure.

Step 1: Verify Configuration Schema

bash

Check actual loaded configuration

cat ~/.openclaw/openclaw.json | jq ‘.channels.telegram.commands’

Step 2: Apply Correct Configuration

Edit your openclaw.json:

json // BEFORE (incorrect - may be wrong path or type) { “channels”: { “telegram”: { “commands”: { “native”: true } } } }

// AFTER (correct schema) { “channels”: { “telegram”: { “commands”: { “native”: “auto” } } } }

Note: Use the string "auto" instead of boolean true. The code compares against string literals 'auto' and true (string), not boolean true.

Step 3: Restart Gateway

bash

macOS LaunchAgent

launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist launchctl load ~/Library/LaunchAgents/ai.openclaw.gateway.plist

Linux systemd

sudo systemctl restart openclaw-gateway

Docker

docker restart openclaw-gateway

Direct (development)

pkill -f openclaw-gateway && openclaw gateway start


Fix Strategy 2: Manual Command Registration (Immediate)

For immediate relief while investigating the startup issue:

Step 1: Identify Required Commands

bash

List all registered commands from OpenClaw

grep -r “BOT_COMMANDS|registerCommand|command.*description”
~/.openclaw/node_modules/openclaw/dist/ 2>/dev/null
| head -30

Step 2: Register via Telegram API

bash

Replace with your bot token

curl -X POST “https://api.telegram.org/bot/setMyCommands”
-H “Content-Type: application/json”
-d ‘{ “commands”: [ {“command”: “help”, “description”: “Show available commands and usage”}, {“command”: “status”, “description”: “Display system status and health”}, {“command”: “new”, “description”: “Start a new conversation thread”}, {“command”: “reset”, “description”: “Reset conversation context”}, {“command”: “model”, “description”: “Show or change the active AI model”}, {“command”: “skills”, “description”: “List available skill plugins”} ], “scope”: {“type”: “default”} }’

Step 3: Verify Registration

bash curl -s “https://api.telegram.org/bot/getMyCommands” | jq .

Expected output: json { “ok”: true, “result”: [ {“command”: “help”, “description”: “Show available commands and usage”}, {“command”: “status”, “description”: “Display system status and health”} ] }


Fix Strategy 3: Environment Variable Override

If the configuration path is correct but the provider still skips registration:

Step 1: Set Debug Environment Variable

bash

Add to your shell profile or LaunchAgent plist EnvironmentVariables

export OPENCLAW_TELEGRAM_COMMANDS_DEBUG=1 export DEBUG=openclaw:telegram:commands

Step 2: Clear Runtime Cache

bash rm -rf ~/.openclaw/cache/* rm -rf ~/.openclaw/runtime/state.json

Step 3: Restart with Verbose Logging

bash

Run with debug output

openclaw gateway start –log-level debug 2>&1 | grep -i “command|setMy”


Fix Strategy 4: Code-Level Hotfix

If you have access to modify the runtime code:

Step 1: Locate the Provider File

bash

Find the Telegram provider

find ~/.openclaw/node_modules -name “provider-telegram*.js” -o
-name “TelegramProvider.js” 2>/dev/null

Step 2: Patch the Conditional Check

Locate the early-return condition and fix the comparison:

javascript // BEFORE (buggy) if (config.commands?.native !== ‘auto’ && config.commands?.native !== true) { return early; }

// AFTER (fixed) if (config.commands?.native === false || config.commands?.native === ‘false’) { return early; }

Step 3: Add Error Logging to Catch Block

javascript // Find the setMyCommands call and add logging try { await bot.api.setMyCommands(retryCommands); logger.info(TelegramProvider: Registered ${retryCommands.length} native commands); } catch (err) { logger.error(‘TelegramProvider: Failed to register commands’, { error: err.message }); // Don’t swallow - let it propagate }


Fix Strategy 5: Provider Start Order Fix

If the issue is a race condition with plugin loading:

Step 1: Add Startup Dependency

For LaunchAgent (ai.openclaw.gateway.plist): xml KeepAlive SuccessfulExit StartInterval 5

For systemd (openclaw-gateway.service): ini [Unit] After=network.target After=time-sync.target Wants=time-sync.target

[Service] ExecStartPre=/bin/sleep 3 Restart=on-failure RestartSec=5

๐Ÿงช Verification

Immediate Verification Steps

Step 1: Check Gateway Logs for Command Registration

bash

Tail logs with command registration filter

tail -f /var/log/openclaw/gateway.log 2>/dev/null | grep -E “setMyCommands|commands.*registered|native.*command”

Or for recent startup

grep -E “setMyCommands|commands.*registered|native.*command”
/var/log/openclaw/gateway.log | tail -20

Expected output:

[TIMESTAMP] INFO TelegramProvider: Initializing native command registration [TIMESTAMP] INFO TelegramProvider: Built-in commands loaded: help, status, new, reset, model [TIMESTAMP] INFO TelegramProvider: Skill commands loaded: 4 commands [TIMESTAMP] INFO TelegramProvider: Calling setMyCommands with 8 commands [TIMESTAMP] INFO TelegramProvider: Successfully registered 8 bot commands via setMyCommands

Actual output (bug still present):

No matching entries found

Step 2: Verify via Telegram API

bash

Replace with your bot token

TELEGRAM_TOKEN=“123456789:ABCdefGHI…”

curl -s “https://api.telegram.org/bot${TELEGRAM_TOKEN}/getMyCommands" | jq .

Expected: json { “ok”: true, “result”: [ { “command”: “help”, “description”: “Show available commands” }, { “command”: “status”, “description”: “Display system status” } ] }

Actual (unfixed): json { “ok”: true, “result”: [] }

Step 3: Test Command Menu in Telegram

  1. Open a DM with your Telegram bot
  2. Type / โ€” the command menu should appear with autocomplete
  3. Try each command: /help, /status, /new, /reset

Expected: Command menu appears immediately when typing /

Actual (unfixed): No menu appears; commands must be typed manually


Automated Verification Script

bash #!/bin/bash

verify-telegram-commands.sh

TELEGRAM_TOKEN="${OPENCLAW_TELEGRAM_TOKEN:-}”

if [ -z “$TELEGRAM_TOKEN” ]; then echo “ERROR: OPENCLAW_TELEGRAM_TOKEN not set” exit 1 fi

echo “=== Telegram Bot Commands Verification ===” echo ""

Check registered commands

RESPONSE=$(curl -s “https://api.telegram.org/bot${TELEGRAM_TOKEN}/getMyCommands") COUNT=$(echo “$RESPONSE” | jq ‘.result | length’)

echo “Registered commands: $COUNT” echo “$RESPONSE” | jq -r ‘.result[] | " /” + .command + " - " + .description’

if [ “$COUNT” -gt 0 ]; then echo "" echo “โœ“ SUCCESS: Commands are registered” exit 0 else echo "" echo “โœ— FAILURE: No commands registered” exit 1 fi

Run the verification: bash chmod +x verify-telegram-commands.sh ./verify-telegram-commands.sh

Expected output:

=== Telegram Bot Commands Verification ===

Registered commands: 6 /help - Show available commands /status - Display system status /new - Start a new conversation /reset - Reset conversation context /model - Show or change AI model /skills - List skill plugins

โœ“ SUCCESS: Commands are registered


Log Analysis Checklist

After applying fixes, verify these log entries appear at startup:

Log EntryStatusMeaning
TelegramProvider: Initializing native command registrationโœ“ RequiredProvider detected native commands enabled
TelegramProvider: Built-in commands loadedโœ“ RequiredCore commands enumerated
TelegramProvider: Skill commands loaded: N commandsโœ“ If nativeSkills enabledPlugin commands loaded
TelegramProvider: Calling setMyCommands with N commandsโœ“ RequiredAPI call initiated
TelegramProvider: Successfully registered N bot commandsโœ“ RequiredAPI call succeeded

โš ๏ธ Common Pitfalls

Configuration Pitfalls

1. Type Coercion Mismatch

json // INCORRECT - boolean instead of string “native”: true

// CORRECT - string value “native”: “auto”

The provider code likely compares against string literals. Boolean true will never equal the string "true" in JavaScript.

2. Nested Path Errors

json // INCORRECT - wrong nesting “telegram”: { “plugin”: { “commands”: { “native”: true } } }

// CORRECT - expected path “telegram”: { “commands”: { “native”: “auto” } }

3. Environment Variable Override Ignored

bash

Set in shell but not passed to LaunchAgent

export OPENCLAW_TELEGRAM_COMMANDS_ENABLED=true # Ignored by LaunchAgent

Correct: Add to plist EnvironmentVariables

EnvironmentVariables OPENCLAW_TELEGRAM_COMMANDS_ENABLED true

Platform-Specific Traps

macOS LaunchAgent

PitfallSymptomSolution
Config cached by launchdChanges don’t take effectRun launchctl unload/load cycle
User-level vs system plistPermission deniedCheck ~/Library/LaunchAgents/ vs /Library/LaunchAgents/
Stale PID preventing restart“Already running”launchctl remove ai.openclaw.gateway && launchctl load

Docker Container

PitfallSymptomSolution
Volume mount orderConfig not foundMount config volume before application volume
ENV var timingOverrides not appliedUse docker-compose.override.yml
Network isolationAPI calls failEnsure container can reach api.telegram.org

Linux Systemd

PitfallSymptomSolution
Service starts too earlyRace conditionAdd ExecStartPre=/bin/sleep 5
Journal truncationLogs missingjournalctl -u openclaw-gateway -n 1000
SELinux/AppArmorNetwork blockedsetsebool -P nis_enabled 1

Runtime Pitfalls

1. Token Mismatch

If the bot token used in openclaw.json differs from the token in environment variables, getMyCommands and setMyCommands will reference different bots.

bash

Verify token consistency

jq ‘.channels.telegram.token’ ~/.openclaw/openclaw.json echo $OPENCLAW_TELEGRAM_TOKEN

2. BotFather Command Scope

Telegram bots can have commands registered at different scopes:

  • default - All chats
  • commands_in_private - Only private chats
  • Per-chat scope with chat_id

If commands were previously set with a scope via BotFather, setMyCommands may not override them.

bash

Clear all command scopes

curl -X POST “https://api.telegram.org/bot/deleteMyCommands”
-H “Content-Type: application/json”
-d ‘{“scope”: {“type”: “all_private_chats”}}’

curl -X POST “https://api.telegram.org/bot/deleteMyCommands”
-H “Content-Type: application/json”
-d ‘{}’ # Delete default scope too

3. Rate Limiting

Telegram rate limits setMyCommands to approximately 1 call per second. If the gateway restarts rapidly during debugging, the API may reject calls.

bash

Check for rate limit errors

grep -i “Too Many Requests|retry_after” /var/log/openclaw/gateway.log

4. Bot Mode Restrictions

Bots in certain modes (e.g.,_inline-only, restricted) cannot use setMyCommands.

Development Pitfalls

Hot Reload Misses Provider

Some hot-reload implementations reinitialize plugins but not the Telegram provider. Commands are never re-registered.

bash

Full restart required, not just config reload

pkill -f openclaw openclaw gateway start

Debug Logging Silences Errors

High logLevel settings (debug, trace) can sometimes cause errors to be logged at a level that’s filtered out.

bash

Explicitly enable all Telegram-related logs

openclaw gateway start –log-level debug –log-filter “telegram,commands,provider”

Error CodeDescriptionConnection
BOT_COMMANDS_TOO_MUCHTelegram API error when exceeding 100 commandssetMyCommands fails when registering too many skills
TELEGRAM_API_ERROR_400Bad request to Telegram APIMalformed command payload to setMyCommands
TELEGRAM_API_ERROR_401UnauthorizedInvalid bot token prevents setMyCommands
TELEGRAM_API_ERROR_429Too many requestsRate limiting on setMyCommands calls
IssueDescriptionLikelihood
lossless-claw context engine errorsLCM errors during restart (noted in issue)Secondary issue, resolved independently
Plugin initialization timingSkills load after provider initRace condition pattern seen in v2026.x
Config schema changes between versionsBreaking config format changesCommon in milestone releases
Provider lifecycle changesTelegram provider refactor in v2026.4Direct cause of code path bug

Similar Symptoms in Other Channels

ChannelRelated SymptomDifferentiator
DiscordSlash commands not registeringDifferent API (/applications endpoint)
SlackApp home commands missingUses apps.connections.install flow
MatrixCommand prefixes not showingUses room state events instead

External References

  • Telegram Bot API: setMyCommands โ€” https://core.telegram.org/bots/api#setmycommands
  • Telegram Bot API: getMyCommands โ€” https://core.telegram.org/bots/api#getmycommands
  • grammY Commands Plugin โ€” https://grammy.dev/plugins/commands
  • OpenClaw Config Schema โ€” ~/.openclaw/node_modules/openclaw/schema/config.json

Search for these patterns in gateway logs when diagnosing similar issues:

bash

All Telegram-related logging

grep -E “TelegramProvider|Telegram.*provider|telegram” /var/log/openclaw/gateway.log

Command registration attempts

grep -E “setMyCommands|register.*command|command.*registration” /var/log/openclaw/gateway.log

API errors

grep -E “api.*error|api.*fail|telegram.*error” /var/log/openclaw/gateway.log

Configuration loading

grep -E “config.*load|channels.*init|provider.*start” /var/log/openclaw/gateway.log

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.