Skip to content

[Bug] claude-cli backend: synthetic "No response requested." placeholder leaves Telegram turn fully silent with no observability #90789

Description

@wangwllu

Summary

When the bundled claude-cli backend hits AbortError / no-output / wedged-session conditions, Claude Code CLI injects a synthetic assistant message with model: "<synthetic>" and content "No response requested.". OpenClaw's stream-json parser does not extract text from assistant message blocks (only tool_use / tool_result), and the trailing type: "result" event commonly carries an empty result string in this case. The turn is then classified as empty and dropped silently. The user sees no message, no status, no retry indicator.

Why it matters

End users can't distinguish "OpenClaw is stuck" from "model decided not to reply." In a long-running multi-step session (workflows, deep-research, PDF generation), this consistently happens after large tool-heavy turns. The user has to manually probe with "are you there?" to trigger a fresh turn.

Reproduction shape

  • backend: claude-cli/Claude-Opus-4.7 (bundled @anthropic-ai/claude-code)
  • channel: telegram (group with topics, but also reproduces in DM)
  • trigger: long context turn after a heavy tool sequence (e.g. background workflow + HTML/PDF generation) followed by a new user message
  • transcript line shape:
    {
      "type": "assistant",
      "message": {
        "model": "<synthetic>",
        "role": "assistant",
        "content": [{"type": "text", "text": "No response requested."}],
        "stop_reason": "stop_sequence",
        "usage": {"input_tokens": 0, "output_tokens": 0}
      }
    }
  • Observed 4 times in a single 4h conversation on 2026-06-05

Root cause (from code inspection)

  1. src/agents/cli-output.ts:573-602 — assistant message handler only iterates tool_use / tool_result blocks, no text extraction. Synthetic-message text is silently dropped at parse time.
  2. src/agents/cli-output.ts:374-388type: "result" extractor returns {text: ""} when result string is empty (a real codepath for tool-only turns).
  3. src/agents/embedded-agent-runner/result-fallback-classifier.ts:11-13hasDeliberateSilentTerminalReply calls isSilentReplyPayloadText, which only matches NO_REPLY (case-insensitive). No response requested. is not in the silent-token set.
  4. result-fallback-classifier.ts:73if (!isGpt5ModelId(params.model)) return null — non-GPT-5 backends never trigger the auto-fallback/retry branch for empty/synthetic results.
  5. Net effect: empty turn → no outbound delivery, no retry, no user-visible signal.

Background on the upstream placeholder

No response requested. is a Claude Code CLI synthetic assistant message injected by ensureToolResultPairing and similar repair paths, exported as NO_RESPONSE_REQUESTED in src/utils/messages.ts of Claude Code, marked with model: "<synthetic>" (sources: Yuyz0112/claude-code-reverse, claude-code-best/claude-code). It's a well-known noise string filtered out by third-party Claude Code GUIs (e.g. sting8k/pi-vcc, edmundmiller/dotfiles).

Proposed fix

Minimum (correctness)

  1. In cli-output.ts parseClaudeStreamJsonResult, detect parsed.type === "assistant" with message.model === "<synthetic>" and content text matching the known synthetic-placeholder set ("No response requested.", "Continue from where you left off."). Tag the result with a new classification, e.g. synthesized-placeholder.
  2. In result-fallback-classifier.ts, treat synthesized-placeholder as hasDeliberateSilentTerminalReply === true AND additionally surface a structured status signal back to the channel adapter (so Telegram can render a status reaction or one-shot debug breadcrumb).

Nice-to-have (observability)

  1. Lift the GPT-5-only restriction on auto-retry in classifyEmbeddedAgentRunResultForModelFallback, or at least extend it to classify synthesized-placeholder as retryable for claude-cli backends (Claude CLI's own AbortError path already produces these placeholders; the retry could be one-shot to avoid loops).
  2. Add a silentErrorReplies-style status surface for synthetic-placeholder turns so users can see "🟡 backend produced no output, no retry" instead of nothing.

Related

Environment

  • OpenClaw 2026.6.1
  • claude-cli/Claude-Opus-4.7
  • macOS Darwin 25.4.0 (arm64)
  • node v26.0.0
  • channel: telegram (group + topic)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions