You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
openclaw 2026.5.28 reintroduces the same reasoning-content leak that was already reported and resolved in #6470 (closed 2026-02-25). On QQBot, the model’s internal reasoning / chain-of-thought / self-narration is being sent as the visible message body instead of being hidden from the user.
This issue specifically covers the QQBot channel, which was not addressed by the original #6470 fix (that one focused on Discord). I am opening a separate report because the QQBot plugin has no sanitizeText hook and is therefore fully exposed.
Environment
OpenClaw version: 2026.5.28 (upgraded from 2026.5.12, where this did not occur)
Last working version: 2026.5.12
Install method: official npm i -g openclaw
Channel: official @tencent-connect/openclaw-qqbot plugin
Install OpenClaw 2026.5.28 (official npm) and the official QQBot plugin
Configure channels.qqbot with appId, clientSecret, enabled: true, streaming: true
Configure a model with reasoning: true (e.g. minimax/MiniMax-M3 via anthropic-messages) and agents.defaults.thinkingDefault: "medium"
Send any non-trivial message to the QQ bot (one that triggers tool calls, code execution, or multi-step planning)
Observe the QQ bot reply
Expected behavior (per 2026.5.12)
Only the model’s final user-facing answer is delivered to QQBot. Internal reasoning, planning, self-narration ("Let me think…", "The user is saying…", "I need to check…", etc.) must be filtered out, as was the case in 5.12 and as was the behavior restored by the fix in #6470.
Actual behavior (in 2026.5.28)
The model’s entire reasoning trace is delivered to QQBot as visible message content. The user sees the agent’s internal monologue alongside (or in place of) the real answer. For example, a single reply can contain the agent literally narrating its own debugging process, the bash-quoting mistake it discovered, and the security concerns it considered — all before the actual answer. This is the same failure mode as #6470.
Root cause analysis
I diffed the 5.12 and 5.28 release notes and the change appears to be intentional, but the QQBot plugin (which has no sanitizeText hook) was not accounted for.
5.12 (last working version) — explicit strip behavior
The 5.12 release notes contain explicit reasoning-stripping fixes:
Anthropic-compatible: strip replayed thinking blocks for custom Anthropic-compatible models that explicitly declare supportsReasoningEffort: false
OpenAI-compatible models: strip prior assistant reasoning fields from replayed Chat Completions history by default, preventing oMLX/vLLM Qwen follow-up turns from rejecting or stalling on stale reasoning fields
Telegram: keep verbose tool progress and result drafts separate from the final assistant answer so tool output no longer blends into the final Telegram message (#80294)
Telegram: delete tool-progress-only draft bubbles before rotating to the real answer, preventing orphaned progress messages in streamed replies
In 5.12, the default behavior was to strip thinking blocks / tool-progress drafts from the final outbound payload.
5.28 — preserve / display behavior
5.28 reverses that direction in several places:
Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek reasoning_content replay across tier suffixes…
Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. (#85200)
Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. (#82823, #87165) — the plugin reply pipeline was rewritten, but QQBot has no sanitizeText in its outbound config
In 5.28, the default behavior changed to preserve signed thinking payloads and Discord actively displays commentary in progress drafts. Discord / Telegram each have their own progress-draft handling so the regression is hidden there, but QQBot does not.
Why QQBot is the canary
Looking at node_modules/@openclaw/qqbot/dist/channel-8Efx0wKu.js, the QQBot outbound config defines chunker, chunkerMode, textChunkLimit, shouldSuppressLocalPayloadPrompt, sendText, and sendMedia — but no sanitizeText function. Compare with Discord and Google Chat which both pass sanitizeText: ({ text }) => sanitizeForPlainText(text) through their channel-*.js files. In 5.12, the core runtime’s strip-thinking-blocks pass compensated for QQBot’s missing sanitizeText. In 5.28, that pass is gone, so QQBot ships the raw model output.
This is also visible in the same release batch under Channels:
cap Telegram, Discord, WhatsApp, Signal, Feishu, Google Chat, Microsoft Teams, QQBot, Nostr, Zalo, Zalouser, and Nextcloud-style request/retry timers — QQBot is in the same list as the rest, but did not receive the equivalent thinking-block filtering that Discord/Telegram/Feishu have.
Suggested fixes
Any one of the following would resolve this for QQBot. I would prefer (1) because it’s the most general and most consistent with the fix path used for Discord/Telegram/Feishu.
Restore a default sanitizeText pass for channels that don’t define one. In the deliver / normalizePayloadsForChannelDelivery path, when outbound.sanitizeText is missing, fall back to sanitizeAssistantVisibleText (or an equivalent) so that hidden reasoning blocks and tool-progress drafts are stripped before any plugin sees the payload. This makes the QQBot regression self-healing and protects any future plugin that doesn’t ship its own sanitizer.
Bring back a thinking-block strip in the assistant-visible-text pass. In 5.12, the strip applied to Anthropic-compatible providers that declared supportsReasoningEffort: false. Either:
make that the default again, or
have the provider-specific block in providers/agents explicitly skip models with supportsReasoningEffort: false (matching the 5.12 wording) so users who set that flag actually get the strip they were promised in 5.12.
Ship a sanitizeText in the QQBot plugin’s outbound config. This is the smallest patch but only fixes QQBot, and any future plugin that forgets the field will regress the same way.
Document sanitizeText as required for plugin authors. Less ideal but at least surfaces the trap.
A combination of (1) and (2) would match the 5.12 default and prevent the next regression of this kind.
Workaround
I have not changed my config to work around this (per the 5.12 → 5.28 diff, every available workaround either disables the model’s reasoning entirely, which I rely on for the Feishu side, or requires editing the QQBot plugin source). I am waiting on a fix before upgrading past 5.12.
6.1-beta.3 release notes already mention suppress disabled Ollama reasoning output, strip Kimi-incompatible Anthropic cache markers — so the maintainers are aware reasoning-stripping is still a thing; the QQBot case just isn’t covered yet.
Summary
openclaw 2026.5.28reintroduces the same reasoning-content leak that was already reported and resolved in #6470 (closed 2026-02-25). On QQBot, the model’s internal reasoning / chain-of-thought / self-narration is being sent as the visible message body instead of being hidden from the user.This issue specifically covers the QQBot channel, which was not addressed by the original #6470 fix (that one focused on Discord). I am opening a separate report because the QQBot plugin has no
sanitizeTexthook and is therefore fully exposed.Environment
2026.5.28(upgraded from2026.5.12, where this did not occur)2026.5.12npm i -g openclaw@tencent-connect/openclaw-qqbotpluginminimax/MiniMax-M3(anthropic-compatible,api: "anthropic-messages",reasoning: true,thinkingDefault: "medium")~/.openclaw/npm/projects/openclaw-qqbot-d3553f72f8/node_modules/@openclaw/qqbot/Steps to reproduce
channels.qqbotwithappId,clientSecret,enabled: true,streaming: truereasoning: true(e.g.minimax/MiniMax-M3via anthropic-messages) andagents.defaults.thinkingDefault: "medium"Expected behavior (per 2026.5.12)
Only the model’s final user-facing answer is delivered to QQBot. Internal reasoning, planning, self-narration (
"Let me think…","The user is saying…","I need to check…", etc.) must be filtered out, as was the case in 5.12 and as was the behavior restored by the fix in #6470.Actual behavior (in 2026.5.28)
The model’s entire reasoning trace is delivered to QQBot as visible message content. The user sees the agent’s internal monologue alongside (or in place of) the real answer. For example, a single reply can contain the agent literally narrating its own debugging process, the bash-quoting mistake it discovered, and the security concerns it considered — all before the actual answer. This is the same failure mode as #6470.
Root cause analysis
I diffed the 5.12 and 5.28 release notes and the change appears to be intentional, but the QQBot plugin (which has no
sanitizeTexthook) was not accounted for.5.12 (last working version) — explicit strip behavior
The 5.12 release notes contain explicit reasoning-stripping fixes:
Anthropic-compatible: strip replayed thinking blocks for custom Anthropic-compatible models that explicitly declare supportsReasoningEffort: falseOpenAI-compatible models: strip prior assistant reasoning fields from replayed Chat Completions history by default, preventing oMLX/vLLM Qwen follow-up turns from rejecting or stalling on stale reasoning fieldsTelegram: keep verbose tool progress and result drafts separate from the final assistant answer so tool output no longer blends into the final Telegram message (#80294)Telegram: delete tool-progress-only draft bubbles before rotating to the real answer, preventing orphaned progress messages in streamed repliesIn 5.12, the default behavior was to strip thinking blocks / tool-progress drafts from the final outbound payload.
5.28 — preserve / display behavior
5.28 reverses that direction in several places:
Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek reasoning_content replay across tier suffixes…Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. (#85200)Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. (#82823, #87165)— the plugin reply pipeline was rewritten, but QQBot has nosanitizeTextin its outbound configChannels/replies: preserve channel-owned progress callbacks when verbose output is off, keep group-room progress suppression intact, prefer external session delivery context, escape Discord component…In 5.28, the default behavior changed to preserve signed thinking payloads and Discord actively displays commentary in progress drafts. Discord / Telegram each have their own progress-draft handling so the regression is hidden there, but QQBot does not.
Why QQBot is the canary
Looking at
node_modules/@openclaw/qqbot/dist/channel-8Efx0wKu.js, the QQBotoutboundconfig defineschunker,chunkerMode,textChunkLimit,shouldSuppressLocalPayloadPrompt,sendText, andsendMedia— but nosanitizeTextfunction. Compare with Discord and Google Chat which both passsanitizeText: ({ text }) => sanitizeForPlainText(text)through theirchannel-*.jsfiles. In 5.12, the core runtime’s strip-thinking-blocks pass compensated for QQBot’s missingsanitizeText. In 5.28, that pass is gone, so QQBot ships the raw model output.This is also visible in the same release batch under
Channels:cap Telegram, Discord, WhatsApp, Signal, Feishu, Google Chat, Microsoft Teams, QQBot, Nostr, Zalo, Zalouser, and Nextcloud-style request/retry timers— QQBot is in the same list as the rest, but did not receive the equivalent thinking-block filtering that Discord/Telegram/Feishu have.Suggested fixes
Any one of the following would resolve this for QQBot. I would prefer (1) because it’s the most general and most consistent with the fix path used for Discord/Telegram/Feishu.
Restore a default
sanitizeTextpass for channels that don’t define one. In thedeliver/normalizePayloadsForChannelDeliverypath, whenoutbound.sanitizeTextis missing, fall back tosanitizeAssistantVisibleText(or an equivalent) so that hidden reasoning blocks and tool-progress drafts are stripped before any plugin sees the payload. This makes the QQBot regression self-healing and protects any future plugin that doesn’t ship its own sanitizer.Bring back a thinking-block strip in the assistant-visible-text pass. In 5.12, the strip applied to Anthropic-compatible providers that declared
supportsReasoningEffort: false. Either:providers/agentsexplicitly skip models withsupportsReasoningEffort: false(matching the 5.12 wording) so users who set that flag actually get the strip they were promised in 5.12.Ship a
sanitizeTextin the QQBot plugin’soutboundconfig. This is the smallest patch but only fixes QQBot, and any future plugin that forgets the field will regress the same way.Document
sanitizeTextas required for plugin authors. Less ideal but at least surfaces the trap.A combination of (1) and (2) would match the 5.12 default and prevent the next regression of this kind.
Workaround
I have not changed my config to work around this (per the 5.12 → 5.28 diff, every available workaround either disables the model’s reasoning entirely, which I rely on for the Feishu side, or requires editing the QQBot plugin source). I am waiting on a fix before upgrading past 5.12.
Related
suppress disabled Ollama reasoning output, strip Kimi-incompatible Anthropic cache markers— so the maintainers are aware reasoning-stripping is still a thing; the QQBot case just isn’t covered yet.