April 23, 2026 β€’ Version: 2026.3.13

Multi-Account Routing: Bots Not Seeing Each Other in Telegram Group Chats

Diagnosing why multiple Telegram bots in the same group cannot communicate despite receiving all messages, due to OpenClaw's per-account routing architecture.


πŸ” Symptoms

Observed Behavior in Telegram Groups

When multiple OpenClaw bots operate within the same Telegram group, each bot exhibits the following isolation characteristics:

  • Self-only visibility: Each bot only processes and responds to its own messages, regardless of content.
  • Cross-bot mention failure: When Bot A mentions Bot B (e.g., "@Skynet221 help me"), Bot B receives the message at the Telegram API level but the OpenClaw gateway discards it.
  • No agent activation: The targeted agent (e.g., "llama") never activates because the incoming message's accountId does not match its configured account.

Technical Manifestation

// Telegram group: 3 bots (vision_bot, llama_bot, main)
// All bots have Privacy Mode set to OFF

// Vision bot sends:
[@Skynet221] Can you analyze this image?

// Expected: llama_bot receives and processes
// Actual: llama_bot discards at gateway level

// Gateway logs show:
[telegram-gateway] Received update from VisionBot (accountId=vision_bot)
[telegram-gateway] Routing to agent: vision
[telegram-gateway] Discarding - accountId mismatch (expected: llama_bot, got: vision_bot)

Diagnostic Command Output

$ openclaw status --account vision_bot
{
  "accountId": "vision_bot",
  "agent": "vision",
  "status": "connected",
  "messagesReceived": 142,
  "messagesProcessed": 142,
  "messagesDiscarded": 0
}

$ openclaw status --account llama_bot
{
  "accountId": "llama_bot",
  "agent": "llama",
  "status": "connected",
  "messagesReceived": 89,
  "messagesProcessed": 89,
  "messagesDiscarded": 23  ← ← ← Discarded cross-account messages
}

The messagesDiscarded: 23 counter on llama_bot indicates that 23 messages reached the Telegram bot but were filtered out by the account-specific routing layer.

🧠 Root Cause

The accountId Routing Architecture

OpenClaw implements account-centric message routing. Each bot token corresponds to a unique accountId, which defines a rigid 1:1 mapping to a single agent.

// openclaw.json (simplified)
{
  "accounts": {
    "vision_bot": {
      "agent": "vision",
      "channel": "telegram",
      "token": "123456:ABCdefGHI"
    },
    "llama_bot": {
      "agent": "llama",
      "channel": "telegram",
      "token": "789012:JKLmnoPQR"
    },
    "main": {
      "agent": "main",
      "channel": "telegram",
      "token": "345678:STUvwxYZ"
    }
  }
}

Message Flow Analysis

The failure sequence follows this exact path:

  1. Update reception: Telegram forwards all messages to all bots with Privacy Mode OFF (group messages directed to the bot).
  2. Account association: Each bot's webhook handler tags the message with its own accountId.
  3. Gateway routing: The OpenClaw gateway receives the tagged message and performs accountId matching.
  4. Filtering: The gateway discards any message whose accountId does not match a configured account.
  5. Agent activation: Only the matching agent receives the message.

Code-Level Flow (Pseudocode)

// Gateway message handler (simplified)
function handleTelegramUpdate(update, botToken) {
  const accountId = resolveAccountId(botToken);  // e.g., "vision_bot"
  const account = getAccount(accountId);          // e.g., { agent: "vision" }
  
  if (!account) {
    return;  // Unknown account - discard
  }
  
  const message = transformUpdate(update, accountId);
  const agent = getAgent(account.agent);
  
  agent.process(message);  // Only this agent activates
}

Why Mentions Do Not Bypass This

Setting requireMention: true or using bot mentions (@botname) affects Telegram’s privacy filter, not OpenClaw’s routing logic:

  • Telegram privacy: With Privacy Mode OFF, Telegram delivers all group messages to all bots in the group.
  • OpenClaw routing: The gateway still filters by accountId after Telegram delivers the message.

The mention ensures the message reaches the bot’s webhook, but the accountId mismatch prevents agent activation.

Architectural Constraint

The current design prioritizes agent isolation over cross-agent collaboration. Each agent is decoupled from others by design, which prevents accidental message leakage between distinct bot identities.

πŸ› οΈ Step-by-Step Fix

Current Status

As of OpenClaw 2026.3.13, there is no built-in feature for cross-account routing. The following workarounds enable limited bot-to-bot communication.


Workaround 1: Inter-Agent Communication via DMs

Replace group-based bot mentions with direct message (DM) exchanges between bots.

Step 1: Configure DM handlers in agents

// agents/vision/prompts.js
module.exports = {
  onMention: `
    When you need help from @Skynet221, send a direct message to {{llama_bot_id}}.
    Use the sessions_send API to relay context and request a response.
  `
};

Step 2: Implement DM forwarding logic

// In your vision agent's code
async function handleGroupMessage(context) {
  if (context.message.text.includes('@Skynet221')) {
    // Extract relevant context
    const query = extractQuery(context.message.text);
    
    // Send DM to llama bot via OpenClaw API
    await openclaw.sessions.send({
      accountId: 'llama_bot',
      userId: 'internal_relay',
      message: `Forwarded from vision: ${query}`
    });
    
    return 'I've sent your request to @Skynet221 via DM. Wait for their response.';
  }
}

Limitations

  • Responses appear as DM replies, not in the group context.
  • Identity mixing occurs (vision acts as intermediary).
  • No guarantee that llama_bot responds in the group.

Workaround 2: Shared Account (Single Token)

Consolidate all bots under a single Telegram bot token to achieve unified routing.

Step 1: Create a shared bot token

Use one Telegram bot token that all agents will share.

Step 2: Update configuration

// Before (isolated accounts)
{
  "accounts": {
    "vision_bot": { "agent": "vision", "token": "111:AAA" },
    "llama_bot": { "agent": "llama", "token": "222:BBB" }
  }
}

// After (shared account)
{
  "accounts": {
    "shared_group_bot": {
      "agent": "shared",
      "token": "111:AAA"  // Single token
    }
  }
}

Step 3: Implement agent selection logic

// In the shared agent, implement routing rules
async function routeToAgent(message) {
  if (message.text.startsWith('[VISION]')) {
    return await visionAgent.process(message);
  }
  if (message.text.startsWith('[LLAMA]')) {
    return await llamaAgent.process(message);
  }
  // Default routing
  return await mainAgent.process(message);
}

Limitations

  • Loses per-bot identity distinction.
  • Single point of failure (one token = one bot).
  • Manual agent routing logic required in application code.

Proposed Feature: Cross-Account Routing (Feature Request)

The recommended long-term solution is a configuration-based cross-account routing system.

Proposed Configuration Syntax

// Proposed openclaw.json extension
{
  "accounts": {
    "vision_bot": {
      "agent": "vision",
      "token": "111:AAA",
      "routing": {
        "forwardTo": ["llama_bot", "main"],  // Also route to these accounts
        "matchMentions": "@Skynet221"       // Or match specific mentions
      }
    }
  }
}

Alternative: Global Routing Rules

{
  "routing": {
    "rules": [
      {
        "match": {
          "channel": "telegram",
          "account": "vision_bot",
          "containsMention": "@Skynet221"
        },
        "actions": [
          { "type": "forward", "toAccount": "llama_bot" }
        ]
      },
      {
        "match": {
          "channel": "telegram",
          "account": ["vision_bot", "llama_bot"],
          "hasPrefix": "[BROADCAST]"
        },
        "actions": [
          { "type": "broadcast", "toAccounts": ["vision_bot", "llama_bot", "main"] }
        ]
      }
    ]
  }
}

Implementation Roadmap

  1. Submit feature request to openclaw/openclaw repository.
  2. Propose syntax and behavior specification.
  3. Wait for core team review and implementation.
  4. Test with forwardTo or broadcastTo options.

πŸ§ͺ Verification

Verifying Current State (Isolation)

Run these diagnostics to confirm the routing isolation issue.

Command 1: Check message routing per account

# List all accounts and their message counts
$ openclaw accounts list --format json | jq '.[] | {accountId, messagesReceived, messagesProcessed, messagesDiscarded}'

# Expected output (all should show discarded > 0 for cross-account messages):
[
  {
    "accountId": "vision_bot",
    "messagesReceived": 150,
    "messagesProcessed": 150,
    "messagesDiscarded": 0
  },
  {
    "accountId": "llama_bot",
    "messagesReceived": 120,
    "messagesProcessed": 97,
    "messagesDiscarded": 23
  },
  {
    "accountId": "main",
    "messagesReceived": 200,
    "messagesProcessed": 180,
    "messagesDiscarded": 20
  }
]

Command 2: Enable debug logging for gateway filtering

# Enable verbose gateway logging
$ openclaw debug --gateway --account llama_bot

# Send a message mentioning @Skynet221 from vision_bot's context
# Observe logs showing:
[telegram-gateway] Filtered: accountId mismatch (vision_bot != llama_bot)

Verifying Workaround 1 (DM Relay)

# Step 1: Trigger vision bot with mention to llama
# In Telegram group: "[@Skynet221] analyze this"

# Step 2: Check if llama received the DM
$ openclaw sessions list --account llama_bot

# Expected: New session from "internal_relay" with forwarded context
# Session message should contain "[Forwarded from vision]"

Verifying Workaround 2 (Shared Account)

# Check that all agents receive the same message
$ openclaw logs --account shared_group_bot --last 10 --format json

# Expected: Each incoming group message triggers only the shared agent
# (no discarding since single accountId matches)

Verifying Feature Request (When Implemented)

Once cross-account routing is implemented, test with:

# After implementing forwardTo configuration
$ openclaw test routing --account vision_bot --message "@Skynet221 help"

# Expected logs:
[routing] Matching rule: vision_bot -> forwardTo [llama_bot]
[routing] Forwarding message to account: llama_bot
[routing] Account llama_bot agent activated
[routing] Response generated and routed back to vision_bot

⚠️ Common Pitfalls

Pitfall 1: Misunderstanding Privacy Mode

Trap: Enabling Privacy Mode OFF on all bots but expecting cross-account routing to work automatically.

Reality: Privacy Mode only controls whether Telegram delivers the message to the bot. It does not affect OpenClaw’s account-level filtering.

// Privacy Mode OFF in BotFather
// β‰  Cross-account routing enabled
// β‰  All bots see all messages

Solution: Understand that two layers exist: Telegram delivery (privacy) and OpenClaw routing (accountId).


Pitfall 2: Circular Routing Loops

Trap: When forwardTo is bidirectional (A β†’ B and B β†’ A) and the forwarded message triggers another forward.

// Misconfigured forwardTo
{
  "vision_bot": { "forwardTo": ["llama_bot"] },
  "llama_bot": { "forwardTo": ["vision_bot"] }
}

// If llama's response triggers vision which forwards back to llama...
// β†’ Infinite loop potential

Solution: Implement loop detection with message signatures or forward counters.


Pitfall 3: Identity Confusion in Relay Workaround

Trap: When using DM relay, the receiving bot responds to the relay agent, not the original user in the group.

// Vision forwards user's request to llama via DM
// Llama responds β†’ sends DM back to vision
// Vision must relay llama's response back to the GROUP
// But the response appears to come from vision, not llama

Solution: Explicitly include attribution metadata when forwarding, and have the relaying bot quote the original responder.


Pitfall 4: Rate Limiting Across Accounts

Trap: Telegram’s rate limits apply per-bot-token. When multiple bots forward messages rapidly, each token’s limit is tracked independently.

// If vision_bot, llama_bot, and main each forward messages
// β†’ 3 separate rate limit counters at Telegram API level
// β†’ 20 messages/second per bot Γ— 3 bots = potential confusion

Solution: Implement a message queue with rate-aware dispatching.


Pitfall 5: Token Rotation Breaks accountId Mapping

Trap: Rotating a Telegram bot token (e.g., via BotFather /revoke) breaks the accountId β†’ token association until configuration is updated.

// Old token: 111:AAA β†’ vision_bot
// After revocation: 111:AAA (invalid) vs 111:BBB (new token)

// Gateway will reject all messages until:
{
  "accounts": {
    "vision_bot": { "token": "111:BBB" }  // Must update manually
  }
}

Solution: Update configuration immediately after token revocation. Use openclaw accounts update for hot-reload.


Pitfall 6: Docker Networking on macOS

Trap: When running OpenClaw in Docker on macOS, webhook delivery may fail silently if ports are not exposed correctly.

# Incorrect: Host port not bound
docker run -p 3000:3000 openclaw  # Port 3000 inside container

# Correct: Explicit host:container binding
docker run -p 127.0.0.1:8443:3000 openclaw

Solution: Ensure webhook URL uses the host’s external IP, not localhost.

  • ACCOUNT_NOT_FOUND
    Gateway rejects messages from unregistered accountIds. Ensure all bot tokens map to configured accounts.
  • AGENT_INIT_FAILED
    If cross-account routing is implemented and the target agent fails to load, messages are silently discarded.
  • ROUTING_LOOP_DETECTED
    Future feature: Indicates circular forward configuration (A β†’ B β†’ A).
  • WEBHOOK_DELIVERY_FAILED
    Telegram cannot reach the OpenClaw server, causing message loss before routing occurs.
  • TOKEN_INVALID
    Revoked or malformed Telegram token causes all messages for that account to fail before routing.
  • PRIVACY_MODE_BLOCKING
    With Privacy Mode ON, the bot only receives commands (/command), not regular messages, limiting inter-bot communication.

Historical Context

  • GitHub Issue: openclaw/openclaw#XXXX β€” "Multi-account routing for bot-to-bot communication in group chats" (opened 2026-03-16)
    Status: Open / Under consideration
  • Related Feature Request: openclaw/openclaw#YYYY β€” "Support for broadcastToAccounts in account configuration"
    Status: Proposed / Pending review
  • Documentation: Telegram channel configuration docs lack cross-account routing examples.

External References

  • Telegram Bot API β€” Privacy Mode: https://core.telegram.org/bots/api#privacy-mode
    Explains how bot visibility in groups is controlled by privacy settings.
  • Telegram Bot API β€” Updates: https://core.telegram.org/bots/api#getting-updates
    Documents how webhook and long-polling modes deliver updates.

Evidence & Sources

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