Bug type
Behavior bug (incorrect output/state without crash)
Summary
Targetless message(action="send") fails with "Action send requires a target" in WebChat sessions, despite docs stating it should be projected into the active chat via the internal source-reply sink.
Environment
- OpenClaw: 2026.5.x (installed as global npm package, minified dist)
- Surface: WebChat (Control UI chat tab / macOS SwiftUI app)
- Session: main agent session, direct WebChat conversation
- Channel config: Telegram configured as the only outbound channel; WebChat has no persisted config section (per docs)
Steps to reproduce
- Start a WebChat session with the main agent (Control UI chat tab).
- During an active run, have the agent call
message(action="send", message="test") with no target, to, channelId, or targets parameter.
- Observe the error.
Actual behavior
The tool returns:
Action send requires a target.
The message is not delivered.
Expected behavior
Per docs/web/webchat.md:
"Harnesses that require visible replies through tools.message still use WebChat as a current-run internal source reply sink. A targetless message.send from that active WebChat run is projected into the same chat and mirrored to the session transcript; WebChat does not become a reusable outbound channel and never inherits lastChannel."
A targetless message.send during an active WebChat run should be projected into the same chat as a visible reply, without requiring an explicit target.
Root cause analysis
Traced through the minified dist bundles. The failure is a three-stage miss:
1. sourceReplyDeliveryMode is never set for WebChat
In the tool resolution module (dist/tool-resolution-*.js), the sourceReplyDeliveryMode is computed as:
const sourceReplyDeliveryMode = params.sourceReplyDeliveryMode ??
(params.inboundEventKind === "room_event" && messageProvider !== "webchat"
? "message_tool_only"
: void 0);
The condition messageProvider !== "webchat" explicitly excludes WebChat. So for WebChat sessions, sourceReplyDeliveryMode is undefined.
2. shouldUseInternalSourceReplySink returns false
In dist/internal-source-reply-*.js, the first guard clause requires:
input.sourceReplyDeliveryMode === "message_tool_only"
Since sourceReplyDeliveryMode is undefined for WebChat, this returns false immediately. The source-reply sink path is never entered.
3. normalizeMessageActionInput throws the target error
In dist/message-action-runner-*.js, the execution flow is:
// line ~1335
if (await shouldUseInternalSourceReplySink(input, params))
return handleInternalSourceReplySendAction({...input, agentId}, params);
// line ~1340 — only reached when source-reply sink returns false
params = normalizeMessageActionInput({ action, args: params, toolContext: input.toolContext });
normalizeMessageActionInput calls actionRequiresTarget("send"), which checks MESSAGE_ACTION_TARGET_MODE["send"] → "to" (not "none"), so it returns true. Since no target was provided, it throws:
throw new Error(`Action ${action} requires a target.`);
Why this is a code-level gap, not a config issue
- WebChat has no persisted config section (confirmed in docs: "Legacy
channels.webchat and gateway.webchat config is retired").
- The
sourceReplyDeliveryMode assignment is hardcoded in the tool-resolution source, not driven by configuration.
- The
shouldUseInternalSourceReplySink function already has the right logic to handle WebChat (its hasCurrentSourceReplyContext function explicitly handles provider === "webchat"), but the sourceReplyDeliveryMode guard prevents it from ever being reached.
Suggested fix
In the tool-resolution source (src/agents/tool-resolution.ts or equivalent), extend the sourceReplyDeliveryMode assignment to also activate for WebChat direct sessions:
const sourceReplyDeliveryMode = params.sourceReplyDeliveryMode ??
(params.inboundEventKind === "room_event" && messageProvider !== "webchat"
? "message_tool_only"
: messageProvider === "webchat"
? "message_tool_only"
: void 0);
This makes targetless message.send calls during an active WebChat run route through handleInternalSourceReplySendAction, which projects the message into the same chat — exactly what the docs describe.
Scope consideration: This activates the source-reply sink for WebChat, allowing targetless sends during an active run only. WebChat still does not become a reusable outbound channel (no lastChannel inheritance), preserving the documented boundary.
Workaround
Until the fix lands, the only options are:
- Include file content inline in the agent's final reply using the
MEDIA: directive (works for file delivery but not for mid-run proactive messages).
- Route through another configured channel (e.g., Telegram) with an explicit target.
- Use
chat.inject from the Gateway API (requires direct WebSocket access, not available from the agent tool layer).
Why this matters
WebChat is the primary interface for Control UI and macOS/iOS apps. The inability to send targetless messages during an active run limits agent UX patterns where the agent needs to deliver intermediate results, progress updates, or file attachments proactively within the same conversation turn. The docs already describe the intended behavior — this is an implementation gap.
Bug type
Behavior bug (incorrect output/state without crash)
Summary
Targetless
message(action="send")fails with "Action send requires a target" in WebChat sessions, despite docs stating it should be projected into the active chat via the internal source-reply sink.Environment
Steps to reproduce
message(action="send", message="test")with notarget,to,channelId, ortargetsparameter.Actual behavior
The tool returns:
The message is not delivered.
Expected behavior
Per
docs/web/webchat.md:A targetless
message.sendduring an active WebChat run should be projected into the same chat as a visible reply, without requiring an explicit target.Root cause analysis
Traced through the minified dist bundles. The failure is a three-stage miss:
1.
sourceReplyDeliveryModeis never set for WebChatIn the tool resolution module (
dist/tool-resolution-*.js), thesourceReplyDeliveryModeis computed as:The condition
messageProvider !== "webchat"explicitly excludes WebChat. So for WebChat sessions,sourceReplyDeliveryModeisundefined.2.
shouldUseInternalSourceReplySinkreturnsfalseIn
dist/internal-source-reply-*.js, the first guard clause requires:Since
sourceReplyDeliveryModeisundefinedfor WebChat, this returnsfalseimmediately. The source-reply sink path is never entered.3.
normalizeMessageActionInputthrows the target errorIn
dist/message-action-runner-*.js, the execution flow is:normalizeMessageActionInputcallsactionRequiresTarget("send"), which checksMESSAGE_ACTION_TARGET_MODE["send"]→"to"(not"none"), so it returnstrue. Since no target was provided, it throws:Why this is a code-level gap, not a config issue
channels.webchatandgateway.webchatconfig is retired").sourceReplyDeliveryModeassignment is hardcoded in the tool-resolution source, not driven by configuration.shouldUseInternalSourceReplySinkfunction already has the right logic to handle WebChat (itshasCurrentSourceReplyContextfunction explicitly handlesprovider === "webchat"), but thesourceReplyDeliveryModeguard prevents it from ever being reached.Suggested fix
In the tool-resolution source (
src/agents/tool-resolution.tsor equivalent), extend thesourceReplyDeliveryModeassignment to also activate for WebChat direct sessions:This makes targetless
message.sendcalls during an active WebChat run route throughhandleInternalSourceReplySendAction, which projects the message into the same chat — exactly what the docs describe.Scope consideration: This activates the source-reply sink for WebChat, allowing targetless sends during an active run only. WebChat still does not become a reusable outbound channel (no
lastChannelinheritance), preserving the documented boundary.Workaround
Until the fix lands, the only options are:
MEDIA:directive (works for file delivery but not for mid-run proactive messages).chat.injectfrom the Gateway API (requires direct WebSocket access, not available from the agent tool layer).Why this matters
WebChat is the primary interface for Control UI and macOS/iOS apps. The inability to send targetless messages during an active run limits agent UX patterns where the agent needs to deliver intermediate results, progress updates, or file attachments proactively within the same conversation turn. The docs already describe the intended behavior — this is an implementation gap.