Skip to content

tools/invoke returns "Tool not available: message" in 2026.4.26 even with healthy telegram channel #74780

@rjdjohnston

Description

@rjdjohnston

OpenClaw version

2026.4.26 (be8c246) on macOS 25.4.0 (Darwin / Apple Silicon), Node 25.9.0.

What I expected

POST /tools/invoke with {"tool":"message","args":{"action":"send","channel":"telegram","target":"<chat-id>","message":"…"}} should deliver a Telegram message via the configured bot, the same way agent chat replies do.

What actually happens

The gateway responds:

{"ok":false,"error":{"type":"not_found","message":"Tool not available: message"}}

…even though:

  • channels.telegram.enabled = true and channels.telegram.botToken is set in openclaw.json
  • The Telegram channel logs [telegram] starting provider and [telegram] sendMessage ok chat=<id> message=N for normal agent chat replies
  • A valid bindings[] entry maps agentId: "main" to match.channel: "telegram" + match.peer.id: "<chat-id>"

I tried sessionKey: "agent:main:telegram:direct:<chat-id>", agentId: "main", and the bare request — all three return the same Tool not available: message error.

Why this matters

The workflow runtime's human_approval nodes call tools.invoke('message', { action: 'send', channel, target, … }) to deliver approval prompts. With this regression, approval messages never reach Telegram and operators only learn an approval is pending if they happen to open the kitchen UI. The durable approval.json file is still written, so the run is recoverable, but the human-loop notification is silently dropped.

Reproduction

  1. Configure a bindings[] entry like:
    { "agentId": "main", "match": { "channel": "telegram", "peer": { "kind": "dm", "id": "<chat-id>" } } }
    with channels.telegram enabled + bot token set.
  2. Verify direct bot delivery works:
    curl -sS https://api.telegram.org/bot<token>/sendMessage \
      -d chat_id=<chat-id> -d text=hello
    ok:true.
  3. Try the message tool through the gateway:
    curl -sS -X POST -H "authorization: Bearer <gateway-token>" \
      -H "content-type: application/json" \
      -d '{"tool":"message","args":{"action":"send","channel":"telegram","target":"<chat-id>","message":"diag"}}' \
      http://127.0.0.1:18789/tools/invoke
    {"ok":false,"error":{"type":"not_found","message":"Tool not available: message"}}.

Notes

  • dist/attempt.tool-run-context-BO5J6jJJ.js still has if (toolName === "message") return action === "send" || action === "thread-reply" — the symbol exists, but /tools/invoke doesn't surface it. Possibly the tool now requires a context the public HTTP path no longer provides (allowlist / agent session), but I couldn't find a knob to enable it.
  • Same behavior with tool: "telegram.send" and tool: "telegram_send".
  • Internal cooperator workaround landed in our recipes extension that falls back to a direct Telegram bot API POST when this returns not_found. That keeps approvals flowing locally while this is being investigated.

Happy to dig deeper if pointed at the right module — wasn't sure whether to look at the channel-side adapter or the gateway tool registration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions