Skip to content

DeepSeek V4: isSignedThinkingBlock misses unsigned thinking blocks — reasoning-only retry fails #85192

Description

@yxyujian98-png

Summary

When DeepSeek V4 produces thinking tokens but no visible text (a "reasoning-only" turn), Gateway's reasoning-only retry with "visible-answer continuation" fails to trigger. Instead, the turn hits llm-idle-timeout and falls through to a blind retry that doesn't carry the "continue from partial turn" instruction, which is ineffective for recovering visible output.

Root Cause

The detection chain relies on isSignedThinkingBlock(), which requires specific signature fields on the thinking block:

function isSignedThinkingBlock(block) {
  if (!isThinkingBlock(block)) return false;
  return block.type === "redacted_thinking"
    || block.signature != null
    || block.thinkingSignature != null
    || block.thought_signature != null;
}

assessLastAssistantMessage() then uses isSignedThinkingBlock to classify the turn:

  • hasSignedThinking && !hasNonThinkingContent"incomplete-text" ✅ triggers retry
  • hasUnsignedThinking"incomplete-thinking" ❌ not caught by isReasoningOnlyAssistantTurn
function isReasoningOnlyAssistantTurn(message) {
  return assessLastAssistantMessage(message) === "incomplete-text";
}

If DeepSeek V4 APIs return thinking blocks without signature, thinkingSignature, or thought_signature fields, the block is classified as "unsigned thinking". The turn gets "incomplete-thinking" — which is not matched by isReasoningOnlyAssistantTurn, so resolveReasoningOnlyRetryInstruction returns null and no continuation retry is attempted.

Instead, the run hits the default llm-idle-timeout (120s) and retries via sameModelIdleTimeoutRetry, which retries the same model but without the REASONING_ONLY_RETRY_INSTRUCTION ("Continue from that partial turn and produce the visible answer now...") — so the model restarts from scratch and may produce only thinking tokens again.

Affected Code

  • selection-BmjEdnnA.jsisSignedThinkingBlock(), assessLastAssistantMessage(), isReasoningOnlyAssistantTurn(), resolveReasoningOnlyRetryInstruction()
  • pi-embedded-CJ87lW5R.js — reasoning-only retry loop

Suggested Fix

Either:

  1. Broaden isReasoningOnlyAssistantTurn to also match "incomplete-thinking" when the model is known to produce thinking blocks without signatures (e.g., DeepSeek V4 OpenAI-compatible API).

  2. Or treat unsigned thinking blocks as signed for provider/model combinations where signatures are not expected — so assessLastAssistantMessage returns "incomplete-text" instead of "incomplete-thinking".

Related

Not the same as #75040 (which is about extra_body field collision WARN noise).

Metadata

Metadata

Assignees

Labels

P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions