-
-
Notifications
You must be signed in to change notification settings - Fork 79.2k
claude-cli thinking-only (end_turn, empty text) turns trigger empty_response model-fallback re-run on a different model #89008
Copy link
Copy link
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.Auth, provider routing, model choice, or SecretRef resolution may break.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Metadata
Metadata
Assignees
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.Auth, provider routing, model choice, or SecretRef resolution may break.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Type
Fields
Give feedbackNo fields configured for issues without a type.
Version
OpenClaw
2026.5.28(claude-cli provider, Node v22.22.1, Linux). Confirmed still present in2026.5.31-beta.3.Summary
When a
claude-cliturn completes normally (stop_reason: "end_turn") but the final assistant message contains only athinkingblock and no text (a valid NO_REPLY), the CLI backend throwsFailoverError({ reason: "empty_response" }). Becauseempty_responseis a fallback-triggering reason, the model-fallback chain then re-runs the turn on the configured fallback model (e.g. an OpenRouter model). The fallback model produces a real reply — but on a different model than configured, with different behavior/instruction-following, and a user-visible↪️ Model Fallback: …banner.This is the same root trigger as the (closed) #82394 and #83231, but a distinct downstream consequence that those fixes did not cover:
finalStatus: success→ user sees the generic "Something went wrong" stub.billingcooldown → profile abort.empty_response→ model-fallback re-run on a different model, yielding a wrong-model reply (not a stub, not a cooldown). See also pi-embedded-runner: empty-reasoning-only completions on openai-codex-responses bypass failover (assistantTexts=[], usage.output>0) #85364 (reasoning-only completions bypass failover) and Thinking-only LLM responses silently dropped (kimi-k2.5-thinking) #64570 (thinking-only silently dropped).A completed
end_turnwith no text is a legitimate "nothing to say", not a transient model failure — it should not trigger the fallback chain.Reproduction signature
Gateway logs around an affected turn:
The claude-cli transcript (
~/.claude/projects/<cwd>/<session>.jsonl) for the affected turn shows the final assistant message as:Clusters on long/heavy reasoning turns (20–45s, dozens–hundreds of stream lines) and under concurrency. Not auth (
reason=authis separate), not rate-limit/overloaded (no such signals), not model-specific (observed on bothclaude-sonnet-4-6andclaude-opus-4-7).Code pointers
cli-runner-*.jsexecuteCliAttempt:model-fallback-*.jsstill classifiesempty_responseas a fallback-eligible reason in2026.5.31-beta.3(alongsiderate_limit/overloaded/timeout/unknown).Suggested fix
When the underlying completion has a clean stop reason (
end_turn/stop) and no text and no tool call, resolve the turn as a valid empty no-reply instead of throwingempty_response→ fallback. Equivalently: excludeempty_responsefrom the fallback-trigger path whenstop_reason === "end_turn". Genuine CLI failures (process error, timeout, parse/format error) already throw via separate paths and should keep their failover.