Skip to content

fix: preserve Anthropic thinking block signatures when switching Claude models#25003

Closed
kunalshaw79 wants to merge 1 commit intoanomalyco:devfrom
kunalshaw79:fix/anthropic-thinking-signature-model-switch
Closed

fix: preserve Anthropic thinking block signatures when switching Claude models#25003
kunalshaw79 wants to merge 1 commit intoanomalyco:devfrom
kunalshaw79:fix/anthropic-thinking-signature-model-switch

Conversation

@kunalshaw79
Copy link
Copy Markdown

@kunalshaw79 kunalshaw79 commented Apr 29, 2026

Issue for this PR

Closes #25005

Also related to #23104 which has a similar fix — see note below.

Type of change

  • Bug fix

What does this PR do?

message-v2.ts computes differentModel = true whenever the current model ID differs from the one that produced a historical message. For reasoning/thinking parts this is wrong: it strips providerMetadata, which is where the Anthropic SDK stores the cryptographic signature field required on every subsequent turn.

Without the signature, Anthropic rejects the request:

messages.N.content.0.thinking.signature: Field required

Changes:

  1. message-v2.ts: Introduce keepReasoningMetadata — true when both old and new model are Anthropic-family (providerID is anthropic, amazon-bedrock, or modelID contains claude). Use this for reasoning parts instead of !differentModel, so signatures survive intra-Anthropic model switches. Non-Anthropic providers still have metadata dropped as before.

  2. transform.ts: Add a safety filter in normalizeMessages for Anthropic-family providers that strips reasoning blocks with a missing/empty signature before the API call. This handles old sessions created before signature capture and any edge case where metadata is unavailable. Dropping such a block is safer than crashing the entire conversation.

Note on #23104: That PR uses a simpler one-line fix (unconditionally pass providerMetadata for all reasoning parts regardless of model). This PR takes a slightly more conservative approach (preserve metadata only for Anthropic-family switches) and adds the transform.ts safety filter. Both fix the same root cause. Happy to close this in favour of #23104 if maintainers prefer the simpler approach.

How did you verify your code works?

  • bun test test/session/message-v2.test.ts — 27/27 pass
  • bun typecheck — no errors
  • Manually reproduced: started session with Opus (extended thinking), sent messages, switched to Sonnet, sent another message. Before fix: crash. After fix: works.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…de models

Bug: Switching from one Claude model to another (e.g. Sonnet → Opus, or
any two distinct claude-* models) caused the session to drop the
providerMetadata from reasoning/thinking parts.  The Anthropic API requires
the 'signature' field on every thinking block echoed back in subsequent turns;
without it the request is rejected with:

  messages.N.content.0.thinking.signature: Field required

Root cause (message-v2.ts line 840):
  const differentModel =
    `${model.providerID}/${model.id}` !== `${msg.info.providerID}/${msg.info.modelID}`

This is true whenever the model ID changes.  Line 963 then drops
providerMetadata for reasoning blocks when differentModel is true:

  ...(differentModel ? {} : { providerMetadata: part.metadata })

The signature is a cryptographic token that Anthropic issues and expects
back — it is valid across all Claude models and must be preserved even
when switching between them.

Fix 1 (message-v2.ts):
  Introduce keepReasoningMetadata alongside differentModel.  It is true
  when both the previous and current model are Anthropic-family (providerID
  is 'anthropic', 'amazon-bedrock', or modelID contains 'claude').  Use
  keepReasoningMetadata for reasoning parts instead of !differentModel so
  the signature survives intra-Anthropic model switches.

Fix 2 (transform.ts — belt and suspenders):
  In normalizeMessages for @ai-sdk/anthropic and @ai-sdk/amazon-bedrock,
  filter out any reasoning part whose providerMetadata.anthropic.signature
  is missing or empty.  This handles sessions created before the signature
  was stored (pre-fix history) and any edge case where a reasoning block
  arrives without a valid signature.  A block without a signature cannot be
  echoed back safely; dropping it is better than crashing the session.

Reproduction:
  1. Start a session with a Claude model that has reasoning (e.g. Opus).
  2. Switch the session to a different Claude model.
  3. Send a new message.
  Before fix: 'thinking.signature: Field required'.
  After fix: conversation continues normally.
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Apr 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search, I found one highly related PR that appears to address a similar issue:

Related PR:

This PR is directly related as it also addresses preserving reasoning/thinking block metadata when switching between models. It may be worth reviewing to ensure there's no overlap or to understand the previous approach to this issue.

There are also several other related PRs that deal with thinking block preservation and reasoning metadata handling (e.g., #14393, #12131), but they appear to focus on different aspects of the problem (like compaction headroom or signature validation) rather than the model-switching scenario.

@github-actions github-actions Bot removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Apr 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@rekram1-node
Copy link
Copy Markdown
Collaborator

We are going to do a more durable and different fix, thanks for trying tho.

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.

bug: Anthropic thinking block signature lost when switching between Claude models

2 participants