[Bug]: WhatsApp replies show in WhatsApp but display NO_REPLY in TUI/Web when AI uses message tool
Summary
When the AI responds to WhatsApp messages using the message tool, the reply is successfully delivered to WhatsApp, but the TUI and Web interfaces display NO_REPLY instead of showing the message content. This breaks the multi-interface consistency that OpenClaw is designed to provide.
Steps to reproduce
- Configure WhatsApp channel with
selfChatMode: true and audio transcription enabled
- Send a voice message to your WhatsApp (e.g., Chinese voice: "你好,今天天气怎么样?")
- AI processes the message, transcribes via Whisper, and responds using the
message tool
- Check the response in three places:
- WhatsApp app: ✅ Reply received
- TUI (
pnpm openclaw chat): ❌ Shows NO_REPLY
- Web interface (
http://127.0.0.1:18789/chat?session=agent%3Amain%3Amain): ❌ Shows NO_REPLY
Expected behavior
All three interfaces (WhatsApp, TUI, Web) should display the same reply content synchronously. Users should be able to view the conversation history in any interface.
Actual behavior
- WhatsApp app: Receives the AI reply successfully (e.g., "🗣️ 识别结果:"艾伦,今天天气怎么样?" 🌤️ 上海当前天气:⛅️ +10°C")
- TUI/Web: Shows
NO_REPLY instead of the actual message content
Additional issue when trying to fix with tools.deny
When attempting to fix this by adding tools.deny: ["message"] to the config:
{
"tools": {
"deny": ["message"]
}
}
The AI still attempts to call the message tool, resulting in:
[openclaw] ⚠️ ✉️ Message: send · +1234567890 failed: Tool message not found
This suggests the message tool is required for core functionality and cannot be gracefully disabled.
Root cause analysis
Phase 1: Anti-duplicate mechanism
The shouldSuppressMessagingToolReplies function in src/auto-reply/reply/reply-payloads.ts detects when the AI sends a message via the message tool to the originating channel. To prevent duplicate messages, it suppresses standard reply payloads:
const suppressMessagingToolReplies = shouldSuppressMessagingToolReplies({
messageProvider: params.messageProvider,
messagingToolSentTargets,
originatingTo: params.originatingTo,
accountId: params.accountId,
});
const replyPayloads = suppressMessagingToolReplies ? [] : filteredPayloads;
Result: WhatsApp receives the tool-sent message, but TUI/Web get empty payloads → NO_REPLY
Phase 2: Tool dependency
When message tool is denied, the AI still attempts to invoke it but receives an error instead of gracefully falling back to direct replies.
Environment
- OpenClaw version: 2026.1.30 (inferred from logs)
- OS: Linux (Arch-based)
- Install method: pnpm
- Channel: WhatsApp Web (via Baileys)
- AI Model: deepseek/deepseek-reasoner
- Audio transcription: Whisper medium model (local)
Configuration
{
"channels": {
"whatsapp": {
"dmPolicy": "allowlist",
"selfChatMode": true,
"allowFrom": ["+1234567890"], // Your phone number
"mediaMaxMb": 50,
"debounceMs": 0
}
},
"tools": {
"media": {
"audio": {
"enabled": true,
"models": [{
"command": "/home/morrowind/whisper-venv/bin/whisper",
"args": ["--model", "medium", "--language", "zh", ...]
}]
}
}
}
}
Logs or screenshots
Successful message tool usage (WhatsApp receives reply)
{
"subsystem": "agent/embedded",
"message": "Tracking pending messaging text: tool=message len=318"
}
{
"module": "web-auto-reply",
"message": "sending message to +1234567890"
}
{
"subsystem": "agent/embedded",
"message": "Committed messaging text: tool=message len=318"
}
When tool is denied (error instead of fallback)
{
"subsystem": "agent/embedded",
"message": "embedded run tool start: tool=message toolCallId=call_00_jWaFB1unVOrVwBD5vb7TFG0Z"
}
{
"subsystem": "agent/embedded",
"message": "embedded run tool end: tool=message toolCallId=call_00_jWaFB1unVOrVwBD5vb7TFG0Z"
}
{
"module": "web-auto-reply",
"text": "[openclaw] ⚠️ ✉️ Message: send · +1234567890 failed: Tool message not found"
}
Proposed solutions
Option 1: Sync to session history (recommended)
Modify shouldSuppressMessagingToolReplies to:
- Still suppress duplicate channel replies (prevent duplicate WhatsApp messages)
- But also copy the message content to the session history (so TUI/Web can display it)
This maintains the anti-duplicate mechanism while ensuring interface consistency.
Option 2: Add WhatsApp systemPrompt support
Currently, only Discord/Slack/Telegram channels support the systemPrompt configuration field (per-channel). Adding this to WhatsApp would allow users to instruct the AI:
{
"channels": {
"whatsapp": {
"systemPrompt": "When replying to messages, respond directly with text. Only use the 'message' tool when explicitly asked to send messages to different contacts or channels."
}
}
}
Option 3: Improve tool deny fallback
When a tool is denied via tools.deny, the system should gracefully fallback to alternative behavior instead of returning a "Tool not found" error.
Additional context
- Detailed debugging log: [Attached as comment or external link]
- Related files:
src/auto-reply/reply/reply-payloads.ts:87-123 - shouldSuppressMessagingToolReplies
src/auto-reply/reply/agent-runner-payloads.ts:115 - Invocation point
src/config/types.whatsapp.ts - WhatsApp config (no systemPrompt field)
src/config/types.discord.ts:40 - Discord config (has systemPrompt field)
- The issue is reproducible with voice messages but likely affects all scenarios where the AI uses the
message tool to reply
Note: This issue was discovered during Chinese voice message transcription testing with local Whisper integration. The audio transcription works perfectly, but the reply synchronization is broken across interfaces.
[Bug]: WhatsApp replies show in WhatsApp but display
NO_REPLYin TUI/Web when AI uses message toolSummary
When the AI responds to WhatsApp messages using the
messagetool, the reply is successfully delivered to WhatsApp, but the TUI and Web interfaces displayNO_REPLYinstead of showing the message content. This breaks the multi-interface consistency that OpenClaw is designed to provide.Steps to reproduce
selfChatMode: trueand audio transcription enabledmessagetoolpnpm openclaw chat): ❌ ShowsNO_REPLYhttp://127.0.0.1:18789/chat?session=agent%3Amain%3Amain): ❌ ShowsNO_REPLYExpected behavior
All three interfaces (WhatsApp, TUI, Web) should display the same reply content synchronously. Users should be able to view the conversation history in any interface.
Actual behavior
NO_REPLYinstead of the actual message contentAdditional issue when trying to fix with
tools.denyWhen attempting to fix this by adding
tools.deny: ["message"]to the config:{ "tools": { "deny": ["message"] } }The AI still attempts to call the
messagetool, resulting in:This suggests the
messagetool is required for core functionality and cannot be gracefully disabled.Root cause analysis
Phase 1: Anti-duplicate mechanism
The
shouldSuppressMessagingToolRepliesfunction insrc/auto-reply/reply/reply-payloads.tsdetects when the AI sends a message via themessagetool to the originating channel. To prevent duplicate messages, it suppresses standard reply payloads:Result: WhatsApp receives the tool-sent message, but TUI/Web get empty payloads →
NO_REPLYPhase 2: Tool dependency
When
messagetool is denied, the AI still attempts to invoke it but receives an error instead of gracefully falling back to direct replies.Environment
Configuration
{ "channels": { "whatsapp": { "dmPolicy": "allowlist", "selfChatMode": true, "allowFrom": ["+1234567890"], // Your phone number "mediaMaxMb": 50, "debounceMs": 0 } }, "tools": { "media": { "audio": { "enabled": true, "models": [{ "command": "/home/morrowind/whisper-venv/bin/whisper", "args": ["--model", "medium", "--language", "zh", ...] }] } } } }Logs or screenshots
Successful message tool usage (WhatsApp receives reply)
{ "subsystem": "agent/embedded", "message": "Tracking pending messaging text: tool=message len=318" } { "module": "web-auto-reply", "message": "sending message to +1234567890" } { "subsystem": "agent/embedded", "message": "Committed messaging text: tool=message len=318" }When tool is denied (error instead of fallback)
{ "subsystem": "agent/embedded", "message": "embedded run tool start: tool=message toolCallId=call_00_jWaFB1unVOrVwBD5vb7TFG0Z" } { "subsystem": "agent/embedded", "message": "embedded run tool end: tool=message toolCallId=call_00_jWaFB1unVOrVwBD5vb7TFG0Z" } { "module": "web-auto-reply", "text": "[openclaw] ⚠️ ✉️ Message: send · +1234567890 failed: Tool message not found" }Proposed solutions
Option 1: Sync to session history (recommended)
Modify
shouldSuppressMessagingToolRepliesto:This maintains the anti-duplicate mechanism while ensuring interface consistency.
Option 2: Add WhatsApp systemPrompt support
Currently, only Discord/Slack/Telegram channels support the
systemPromptconfiguration field (per-channel). Adding this to WhatsApp would allow users to instruct the AI:{ "channels": { "whatsapp": { "systemPrompt": "When replying to messages, respond directly with text. Only use the 'message' tool when explicitly asked to send messages to different contacts or channels." } } }Option 3: Improve tool deny fallback
When a tool is denied via
tools.deny, the system should gracefully fallback to alternative behavior instead of returning a "Tool not found" error.Additional context
src/auto-reply/reply/reply-payloads.ts:87-123-shouldSuppressMessagingToolRepliessrc/auto-reply/reply/agent-runner-payloads.ts:115- Invocation pointsrc/config/types.whatsapp.ts- WhatsApp config (nosystemPromptfield)src/config/types.discord.ts:40- Discord config (hassystemPromptfield)messagetool to replyNote: This issue was discovered during Chinese voice message transcription testing with local Whisper integration. The audio transcription works perfectly, but the reply synchronization is broken across interfaces.