Skip to content

[Bug]: 5.28 regression — reasoning/thinking content leaks into QQBot replies (regression of #6470) #89913

@dygg2001

Description

@dygg2001

Summary

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
  • OS: Linux 5.14.0-689.el9.x86_64 (x64)
  • Node: v22.22.2
  • Provider: minimax/MiniMax-M3 (anthropic-compatible, api: "anthropic-messages", reasoning: true, thinkingDefault: "medium")
  • Install location: ~/.openclaw/npm/projects/openclaw-qqbot-d3553f72f8/node_modules/@openclaw/qqbot/

Steps to reproduce

  1. Install OpenClaw 2026.5.28 (official npm) and the official QQBot plugin
  2. Configure channels.qqbot with appId, clientSecret, enabled: true, streaming: true
  3. Configure a model with reasoning: true (e.g. minimax/MiniMax-M3 via anthropic-messages) and agents.defaults.thinkingDefault: "medium"
  4. Send any non-trivial message to the QQ bot (one that triggers tool calls, code execution, or multi-step planning)
  5. 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
  • Channels/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 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.

  1. 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.

  2. 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.
  3. 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.

  4. 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.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR 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:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.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