Skip to content

[Bug]: Discord message-tool send fails with "Ambiguous Discord recipient" for bare numeric channel IDs (2026.5.22) #86001

@Francois3d

Description

@Francois3d

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

The message tool's Discord outbound send paths throw Ambiguous Discord recipient "<id>"... for bare numeric channel IDs that worked in earlier releases. Channel sessions whose runtime supplies a bare numeric to (no channel: prefix) can no longer reply to their own channel via the message tool.

This appears closely related to the DM-side fixes in #72401 and #75359, but the regression is now on the channel side: a tightened parseDiscordTarget ambiguity check is reached because several outbound send paths drop the defaultKind: "channel" parse option that resolveDiscordTargetChannelId still passes correctly.

Steps to reproduce

  1. Have an OpenClaw 2026.5.22 install with Discord configured and at least one channel/session bound to a guild text channel.
  2. Trigger any LLM-driven message tool send where the model emits { "action": "send", "to": "<bare numeric channel ID>", "message": "..." }. This is the natural form when the model has the channel ID in context but no channel: prefix discipline.
  3. Observe the tool result.

Expected behavior

Outbound text send is delivered to the channel. Historic behavior (and the explicit fast path in resolveDiscordTargetChannelId) is to interpret a bare numeric outbound target as a channel ID.

Actual behavior

Tool result is an error with body:

Ambiguous Discord recipient "<id>". For DMs use "user:<id>" or "<@<id>>"; for channels use "channel:<id>".

In session sessions that auto-relay the channel ID to the tool, this is a permafail: every reply round trips the same error and the channel never sees a response.

OpenClaw version

2026.5.22

Operating system

macOS 26.2

Install method

npm global

Model

claude-opus-4-7 (also reproducible with gpt-5.4 / Codex sessions in the same workspace)

Provider / routing chain

claude-cli (Claude Max), openai-codex/gpt-5.4 — same symptom both sides.

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Full error string surfaced as the message-tool toolResult.content:

Ambiguous Discord recipient "<channelId>". For DMs use "user:<channelId>" or "<@<channelId>>"; for channels use "channel:<channelId>".

Root cause (dist code, 2026.5.22)

dist/target-parsing-D1FhMbX6.js lines 29–32 — bare numeric IDs throw unless options.defaultKind is supplied:

if (/^\d+$/.test(trimmed)) {
    if (options.defaultKind) return buildMessagingTarget(options.defaultKind, trimmed, trimmed);
    throw new Error(options.ambiguousMessage ?? `Ambiguous Discord recipient "${trimmed}". For DMs use "user:${trimmed}" or "<@${trimmed}>"; for channels use "channel:${trimmed}".`);
}
return buildMessagingTarget("channel", trimmed, trimmed);

Outbound send paths that call this without a defaultKind:

File Lines Call
dist/send.outbound-Cy_i_Kpm.js 50, 86 parseAndResolveRecipient(to, cfg, opts.accountId) — main text send
dist/send.components-C0c1TiP_.js 414, 461 components send
dist/send-BASUWZZP.js 727 voice message send
dist/outbound-session-route-CyNVJMI-.js 8, 36 resolveDiscordOutboundTargetKindHint returns undefined for bare numeric input

parseAndResolveRecipient itself signature-defaults parseOptions = {}, so callers that omit the option propagate the empty options all the way to parseDiscordTarget, which then throws.

For contrast, dist/send.shared-DBAwZLzi.js line 650 (resolveDiscordTargetChannelId) is correct — it passes { defaultKind: "channel" } explicitly.

Proposed fix

Either:

  1. Change parseAndResolveRecipient's default to parseOptions = { defaultKind: "channel" } so all current outbound callers fall back to channel-kind for bare numerics, or
  2. Update each outbound send call site (send.outbound, send.components ×2, voice send, and outbound-session-route) to pass { defaultKind: "channel" } explicitly, matching resolveDiscordTargetChannelId.

Option 1 is a one-line restoration of pre-2026.5.20 behavior. The DM-prefix discipline from #72401 / #75359 is preserved because user: / <@id> / discord:user: prefixes still resolve to kind=user regardless of defaultKind.

Impact and severity

  • Severity: High. Discord channel sessions cannot reply to their own channel via the message tool when the model emits a bare numeric to.
  • Frequency: Every reply round trip on affected sessions.
  • Consequence: Discord channels appear silent. Sessions may also stall on isError: true results and burn tokens retrying.

Additional information

Not mentioned in 2026.5.20 or 2026.5.22 release notes. Likely introduced alongside the 2026.5.20 "Agents/message-tool: normalize non-canonical message body aliases" (#84079) and 2026.5.22 "Channels/message tool: resolve configured external channel plugins during in-agent channel selection" (#85022) refactors, both of which touch the same call paths.

Local workaround attempted: none in the affected sessions, since the bare numeric to comes from how the message tool is currently being invoked by the model.

Metadata

Metadata

Assignees

Labels

P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

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