Skip to content

[Bug]: message tool rejects model-generated SendMessage instead of normalizing it to message #84079

@jkoopmann

Description

@jkoopmann

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

OpenClaw 2026.5.18 can lose visible Discord status/final replies when an Anthropic/Pi agent invokes the core message tool with a non-canonical text argument named SendMessage.

The model chooses the correct tool (message) and generally correct routing (target: "channel:<id>"), but emits the send body as SendMessage instead of the expected message. The dispatcher then rejects the call with message required before Discord delivery.

This is separate from the Codex app-server stall issue. In this case, the agent can complete useful work and fail only when trying to report progress or completion back to Discord.

Steps to reproduce

  1. Run OpenClaw with a Discord channel connected.
  2. Use an Anthropic/Pi-backed agent, for example anthropic/claude-sonnet-4-6 or anthropic/claude-opus-4-7.
  3. Ask the agent to perform a multi-step task in Discord where it should send progress or final status via the message tool.
  4. Inspect the session JSONL and gateway logs when no Discord status/result arrives.

Observed tool call shape in JSONL:

{
  "role": "assistant",
  "content": [
    {
      "type": "toolCall",
      "name": "message",
      "arguments": {
        "action": "send",
        "target": "channel:1497109509825626232",
        "SendMessage": "Subagent läuft. Er schreibt die Website komplett als 5-seitige WordPress-Seitenstruktur um..."
      }
    }
  ],
  "provider": "anthropic",
  "model": "claude-sonnet-4-6",
  "stopReason": "toolUse"
}

The same shape was later reproduced with claude-opus-4-7:

{
  "role": "assistant",
  "content": [
    {
      "type": "toolCall",
      "name": "message",
      "arguments": {
        "action": "send",
        "target": "channel:1497109509825626232",
        "SendMessage": "Subagent läuft. Er baut eine echte statische Multi-Page-Site unter `artifacts/test/site/`..."
      }
    }
  ],
  "provider": "anthropic",
  "model": "claude-opus-4-7",
  "stopReason": "toolUse"
}

Expected behavior

OpenClaw should either:

  1. present a tool schema/prompt that reliably causes Anthropic/Pi models to use the canonical message field, or
  2. normalize common text-body aliases before validation.

At minimum, this model-generated call:

{
  "action": "send",
  "target": "channel:1497109509825626232",
  "SendMessage": "Status text"
}

should be canonicalized to:

{
  "action": "send",
  "target": "channel:1497109509825626232",
  "message": "Status text"
}

The Discord channel should then receive the status/final answer.

Actual behavior

The dispatcher rejects the call before delivery:

{
  "status": "error",
  "tool": "message",
  "error": "message required"
}

The Discord channel receives no update. The agent may retry the same broken call shape multiple times. In some runs, the final answer is written only to the transcript and not delivered to Discord.

OpenClaw version

OpenClaw 2026.5.18 (50a2481)

Operating system

Ubuntu

Install method

npm global

Model

claude-opus-4-7

Provider / routing chain

anthropic/claude-opus-4-7 -> OpenClaw Pi/default embedded runner -> core message tool -> Discord channel

Additional provider/model setup details

Anthropic is configured through a local billing proxy:

{
  "models": {
    "providers": {
      "anthropic": {
        "baseUrl": "http://127.0.0.1:18801",
        "api": "anthropic-messages",
        "models": [
          { "id": "claude-sonnet-4-6" },
          { "id": "claude-opus-4-6" },
          { "id": "claude-opus-4-7" }
        ]
      }
    }
  }
}

The Discord target is a channel target and works when addressed as:

channel:1497109509825626232

Control case using the CLI succeeded:

openclaw message send   --channel discord   --target channel:1497109509825626232   --message "..."

That delivered to Discord with message id 1506218532638298142.

Codex-generated message tool calls in the same environment used the canonical field and delivered successfully when the tool call completed:

{
  "name": "message",
  "arguments": {
    "action": "send",
    "target": "channel:1497109509825626232",
    "message": "..."
  }
}

Logs, screenshots, and evidence

Gateway log examples:


2026-05-19T07:24:53.591+00:00 [tools]
message failed: message required
raw_params={
  "action":"send",
  "target":"channel:1497109509825626232",
  "SendMessage":"Subagent läuft..."
}



2026-05-19T08:37:00.283+00:00 [tools]
message failed: message required
raw_params={
  "action":"send",
  "target":"channel:1497109509825626232",
  "SendMessage":"Subagent läuft..."
}



2026-05-19T08:42:33.914+00:00 [tools]
message failed: message required
raw_params={
  "action":"send",
  "target":"channel:1497109509825626232",
  "SendMessage":"Du hattest recht — der Subagent ist nie sauber durchgekommen..."
}



2026-05-19T08:42:42.988+00:00 [tools]
message failed: message required
raw_params={
  "action":"send",
  "target":"channel:1497109509825626232",
  "SendMessage":"Du hattest recht: Subagent ist nie sauber durchgekommen..."
}

Impact and severity

Severity: high for user-facing Discord workflows.

Impact:

  • User sees no progress or final result in Discord even when the agent worked successfully.
  • The agent appears stalled or silent.
  • Status and completion reporting are unreliable for Anthropic/Pi-backed Discord sessions.
  • The issue compounds other runtime failures because even recovery/status messages may fail to deliver.

Additional information

Suggested fix direction: add model-agnostic alias normalization before the message tool send payload is validated.

Pseudo-code:

function normalizeMessageSendArgs(params) {
  if (typeof params.message !== "string" || !params.message.trim()) {
    if (typeof params.SendMessage === "string") params.message = params.SendMessage;
    else if (typeof params.content === "string") params.message = params.content;
    else if (typeof params.text === "string") params.message = params.text;
  }
  return params;
}

This should happen centrally in the core message action path, not only in a model-specific prompt. Different providers/runtimes may generate slightly different argument names.

A diagnostic warning when alias normalization is applied would also help:

[tools] normalized message send alias SendMessage -> message provider=anthropic model=claude-opus-4-7

Workaround for critical status/final delivery:

openclaw message send --channel discord --target channel:<id> --message "..."

Prompting agents with “When using message(action="send"), put the body in message, never SendMessage” may reduce occurrences, but is not robust enough as the only workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingregressionBehavior that previously worked and now fails

    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