Skip to content

fix(sse): recover invalid Anthropic thinking signatures once - #7906

Merged
diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.49from
insoln:fix/anthropic-thinking-signature-recovery-main
Jul 21, 2026
Merged

diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.49from
insoln:fix/anthropic-thinking-signature-recovery-main

Conversation

@insoln

@insoln insoln commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Related issue

Fixes #7899

Summary

Anthropic can reject a historical extended-thinking block with 400 Invalid signature in thinking block. This change performs a bounded recovery after that exact validation failure instead of removing thinking eagerly from every request.

  • sends normal requests unchanged;
  • matches only the exact Anthropic signature-validation 400;
  • removes thinking only from completed historical assistant turns;
  • retries at most once;
  • preserves the complete active tool-use cycle;
  • completes recovery before connection cooldown and provider-breaker accounting.

Why recovery is conditional

Eager removal changes valid same-model requests, alters prompt-cache shape, and can modify Claude Code tool-use history unnecessarily. This patch leaves valid traffic untouched and changes the request only after Anthropic explicitly rejects a signature.

A trailing active cycle can contain several interleaved assistant[thinking, tool_use] → user[tool_result] pairs. Those assistant turns are preserved verbatim. If no completed historical thinking can be removed safely, no recovery retry is sent.

Recovery is not triggered for:

  • generic 400 responses;
  • 429 responses;
  • non-Anthropic providers;
  • Anthropic's separate "thinking blocks in the latest assistant message cannot be modified" error.

If the recovery attempt fails, the response continues through the normal error-classification path without another recovery attempt.

Tests

  • node --import tsx/esm --test tests/unit/anthropic-thinking-signature-recovery.test.ts tests/unit/claude-passthrough-thinking-2454.test.ts
  • node --import tsx/esm --test tests/unit/chatcore-translation-paths.test.ts
  • npm run typecheck:core
  • npm run lint

@insoln
insoln requested a review from diegosouzapw as a code owner July 20, 2026 21:27
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@insoln
insoln force-pushed the fix/anthropic-thinking-signature-recovery-main branch 2 times, most recently from bdd70b1 to 0beb168 Compare July 20, 2026 22:34
@insoln
insoln changed the base branch from main to release/v3.8.49 July 20, 2026 22:41
@insoln
insoln force-pushed the fix/anthropic-thinking-signature-recovery-main branch 2 times, most recently from 158d9d8 to 9054c6b Compare July 20, 2026 22:46
@insoln
insoln force-pushed the fix/anthropic-thinking-signature-recovery-main branch from 9054c6b to ce8acf8 Compare July 20, 2026 22:52
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Reviewed #7906 against release/v3.8.49.

This is a clean, well-scoped fix. The recovery is correctly bounded: it only fires on the exact Anthropic 400 Invalid signature in thinking block validation error (not generic 400s, not 429s, not the separate "latest message cannot be modified" error, not non-Anthropic providers), strips thinking only from completed historical assistant turns, and explicitly preserves the entire active tool_use↔tool_result cycle — including multi-hop interleaved chains, not just the latest turn. It retries at most once and completes before any connection-cooldown / provider-breaker accounting runs, so a successful recovery never pollutes resilience state with a transient, self-corrected error.

I checked out the branch and ran the new test suite plus the two adjacent suites named in the PR body:

  • tests/unit/anthropic-thinking-signature-recovery.test.ts → 9/9 pass
  • tests/unit/claude-passthrough-thinking-2454.test.ts + tests/unit/chatcore-translation-paths.test.ts → 68/68 pass, no regressions

The chatCore.ts wiring (providerFailure: … break providerFailure) mirrors the file's existing T5 intra-family-model-fallback retry pattern closely, which gives me good confidence it's correct even though there's no dedicated handleChatCore()-level integration test for this exact path — the extracted logic (isAnthropicThinkingSignatureError, stripHistoricalThinkingForSignatureRecovery, executeWithAnthropicThinkingSignatureRecovery) is thoroughly unit-tested instead, including the safety case where only active-cycle thinking exists and no retry is sent.

Two small optional suggestions, neither blocking:

  1. isAnthropicThinkingSignatureError hardcodes "anthropic-compatible-" — could reuse the existing ANTHROPIC_COMPATIBLE_PREFIX constant from src/shared/constants/providers.ts.
  2. A handleChatCore()-level integration test (mock fetch 400→200) would directly cover the chatCore.ts wiring itself, on top of the strong unit coverage already present.

CI is fully green and the PR is mergeable against release/v3.8.49. Nice work — thanks for both filing #7899 and fixing it with this level of care.

@diegosouzapw
diegosouzapw merged commit d3f8bbe into cheaper-inference:release/v3.8.49 Jul 21, 2026
15 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Merged — thanks! CI green on the current tip. (Merged on real GitHub CI; a parallel monaco 0.56 bump had left the local validation box with stale node_modules, unrelated to this PR.)

diegosouzapw added a commit to AndrianBalanescu/OmniRoute that referenced this pull request Jul 22, 2026
…bare-model autopick

Scopes the Antigravity 404 model-not-found lockout to the exact model (not the whole
family) so one missing bare model no longer hijacks the family cooldown, plus bare-model
autopick via resolveModelByProviderInference dedup. The thinking-signature-recovery portion
was dropped — cheaper-inference#7899 is already fixed and merged on the release via cheaper-inference#7906.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Jul 22, 2026
…bare-model autopick (#8050)

Scopes the Antigravity 404 model-not-found lockout to the exact model (not the whole
family) so one missing bare model no longer hijacks the family cooldown, plus bare-model
autopick via resolveModelByProviderInference dedup. The thinking-signature-recovery portion
was dropped — #7899 is already fixed and merged on the release via #7906.

Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…bare-model autopick (cheaper-inference#8050)

Scopes the Antigravity 404 model-not-found lockout to the exact model (not the whole
family) so one missing bare model no longer hijacks the family cooldown, plus bare-model
autopick via resolveModelByProviderInference dedup. The thinking-signature-recovery portion
was dropped — cheaper-inference#7899 is already fixed and merged on the release via cheaper-inference#7906.

Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…bare-model autopick (cheaper-inference#8050)

Scopes the Antigravity 404 model-not-found lockout to the exact model (not the whole
family) so one missing bare model no longer hijacks the family cooldown, plus bare-model
autopick via resolveModelByProviderInference dedup. The thinking-signature-recovery portion
was dropped — cheaper-inference#7899 is already fixed and merged on the release via cheaper-inference#7906.

Co-authored-by: AndrianBalanescu <AndrianBalanescu@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(claude): recover rejected historical thinking signatures without eager stripping

2 participants