Skip to content

Bug: Telegram Bot Duplicate Skill Commands in Multi-Agent Setup #5717

@ItamarCoh3n

Description

@ItamarCoh3n

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_2
  • clawdhub + clawdhub_2
  • github + github_2
  • video_frames + video_frames_2
  • weather + weather_2
  • bluebubbles + bluebubbles_2
  • skill_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

  1. First Agent (main):

    • Scans bundled skills → registers github, gog, weather
    • Adds to used set
  2. Second Agent (builder):

    • Scans same bundled skills → finds github, gog, weather
    • Detects conflicts with used set
    • Appends _2 suffix → registers github_2, gog_2, weather_2
  3. Result:

    • Telegram bot has both versions registered
    • User sees duplicate commands in slash menu
  4. Stale Commands:

    • bot.api.setMyCommands() doesn't clear old commands before setting new ones
    • Previously deleted skills persist until manually cleared

Expected Behavior

  1. Single Registration: Each skill command should only be registered once
  2. Default Agent Only: Telegram should only register commands from the default agent
  3. 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

  1. Configure OpenClaw with multiple agents (e.g., main + builder)
  2. Enable Telegram channel with nativeSkills: "auto"
  3. Ensure bundled skills are accessible to both agents
  4. Restart gateway
  5. 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

  1. Should Telegram commands reflect all agents or just the default agent?
  2. Should there be a config option like commands.agentScope: "default" | "all"?
  3. Does this affect other channel plugins (Discord, Slack, etc.)?

Thank you for OpenClaw! 🦞

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions