-
-
Notifications
You must be signed in to change notification settings - Fork 52.7k
Description
Bug Report: Telegram Bot Duplicate Skill Commands
Version: OpenClaw 2026.1.29 (a5b4d22)
Date: 2026-01-31
Reporter: Community User
Summary
When using a multi-agent configuration, Telegram bot commands are duplicated with _2, _3 suffixes (e.g., gog + gog_2, github + github_2). Additionally, commands from deleted skills persist after gateway restart.
Configuration Context
Agents configured:
main(default agent, Haiku 4.5)builder(secondary agent, Sonnet 4.5)
Skills:
- Using bundled skills:
github,gog,clawdhub,weather,video-frames,bluebubbles,skill-creator - All skills are in the global bundled directory, accessible to all agents
Telegram Config:
{
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "allowlist",
"groupPolicy": "disabled"
}
},
"commands": {
"native": "auto",
"nativeSkills": "auto"
}
}Issue Description
After gateway restart, Telegram bot shows duplicate commands:
Duplicates observed:
gog+gog_2clawdhub+clawdhub_2github+github_2video_frames+video_frames_2weather+weather_2bluebubbles+bluebubbles_2skill_creator+skill_creator_2
Stale commands:
travel_agent(from a previously deleted skill, still registered)
Root Cause Analysis
Problem: Multi-Agent Skill Command Registration
File: /dist/telegram/bot-native-commands.js
Function: registerTelegramNativeCommands
Line: ~120
const skillCommands = nativeEnabled && nativeSkillsEnabled
? listSkillCommandsForAgents({ cfg })
: [];Issue: listSkillCommandsForAgents({ cfg }) scans all agents without filtering.
File: /dist/auto-reply/skill-commands.js
Function: listSkillCommandsForAgents
export function listSkillCommandsForAgents(params) {
const used = resolveReservedCommandNames();
const entries = [];
const agentIds = params.agentIds ?? listAgentIds(params.cfg); // ← Gets ALL agents
for (const agentId of agentIds) {
const workspaceDir = resolveAgentWorkspaceDir(params.cfg, agentId);
if (!fs.existsSync(workspaceDir))
continue;
const commands = buildWorkspaceSkillCommandSpecs(workspaceDir, {
config: params.cfg,
eligibility: { remote: getRemoteSkillEligibility() },
reservedNames: used, // ← Conflict detection per-agent
});
for (const command of commands) {
used.add(command.name.toLowerCase());
entries.push(command);
}
}
return entries;
}File: /dist/agents/skills/workspace.js
Function: resolveUniqueSkillCommandName
function resolveUniqueSkillCommandName(base, used) {
const normalizedBase = base.toLowerCase();
if (!used.has(normalizedBase))
return base;
for (let index = 2; index < 1000; index += 1) {
const suffix = `_${index}`;
// ... creates github_2, github_3, etc.
}
}What Happens
-
First Agent (main):
- Scans bundled skills → registers
github,gog,weather - Adds to
usedset
- Scans bundled skills → registers
-
Second Agent (builder):
- Scans same bundled skills → finds
github,gog,weather - Detects conflicts with
usedset - Appends
_2suffix → registersgithub_2,gog_2,weather_2
- Scans same bundled skills → finds
-
Result:
- Telegram bot has both versions registered
- User sees duplicate commands in slash menu
-
Stale Commands:
bot.api.setMyCommands()doesn't clear old commands before setting new ones- Previously deleted skills persist until manually cleared
Expected Behavior
- Single Registration: Each skill command should only be registered once
- Default Agent Only: Telegram should only register commands from the default agent
- Clean Slate: Gateway restart should clear old commands before re-registering
Proposed Fix
Option A: Filter to Default Agent (Recommended)
File: /dist/telegram/bot-native-commands.js
// Before:
const skillCommands = nativeEnabled && nativeSkillsEnabled
? listSkillCommandsForAgents({ cfg })
: [];
// After:
const defaultAgentId = resolveDefaultAgentId(cfg); // Get default agent
const skillCommands = nativeEnabled && nativeSkillsEnabled
? listSkillCommandsForAgents({ cfg, agentIds: [defaultAgentId] })
: [];Rationale:
- Telegram bot operates at the gateway level, not per-agent
- Commands should reflect the default agent's capabilities
- Sub-agents (like builder) are internal, not user-facing
Option B: Global Skill Command Registry
Create a shared command registry that de-duplicates across all agents during registration:
function resolveGlobalSkillCommands(cfg) {
const allCommands = listSkillCommandsForAgents({ cfg });
const uniqueCommands = new Map();
for (const command of allCommands) {
const key = command.name.toLowerCase();
if (!uniqueCommands.has(key)) {
uniqueCommands.set(key, command);
}
}
return Array.from(uniqueCommands.values());
}Option C: Clear Commands Before Re-Registration
File: /dist/telegram/bot-native-commands.js
// Always clear commands first
await withTelegramApiErrorLogging({
operation: "deleteMyCommands",
runtime,
fn: () => bot.api.deleteMyCommands(),
});
// Then set new commands
if (allCommands.length > 0) {
await withTelegramApiErrorLogging({
operation: "setMyCommands",
runtime,
fn: () => bot.api.setMyCommands(allCommands),
});
}Note: Telegram Bot API supports deleteMyCommands to fully clear the command list.
Workaround (User-Side)
Temporary fix until patched:
Add to openclaw.json:
{
"channels": {
"telegram": {
"commands": {
"nativeSkills": false
}
}
}
}This disables skill commands but keeps native OpenClaw commands working.
Reproduction Steps
- Configure OpenClaw with multiple agents (e.g., main + builder)
- Enable Telegram channel with
nativeSkills: "auto" - Ensure bundled skills are accessible to both agents
- Restart gateway
- Check Telegram bot command list (
@BotFather→/mybots→ choose bot → "Edit Bot" → "Edit Commands")
Expected: Single set of commands
Actual: Duplicate commands with _2 suffixes
Additional Context
Multi-agent use case:
- Main agent: Lightweight coordinator (Haiku 4.5)
- Builder agent: Specialized for complex coding tasks (Sonnet 4.5)
- Both agents use same bundled skills for consistency
Impact:
- Confusing UX (users see duplicate commands)
- Namespace pollution (command list cluttered)
- Potential routing issues if wrong command is selected
Logs
No errors in gateway logs during command registration. Commands are registered successfully, just duplicated.
Recommendation
Preferred Solution: Option A (filter to default agent)
Secondary Fix: Option C (clear before re-register) to prevent stale commands
Affected Users:
- Any multi-agent configuration using Telegram
- Likely affects other channel plugins (Discord, Slack) if they use similar command registration
Questions for Maintainers
- Should Telegram commands reflect all agents or just the default agent?
- Should there be a config option like
commands.agentScope: "default" | "all"? - Does this affect other channel plugins (Discord, Slack, etc.)?
Thank you for OpenClaw! 🦞