Skip to content

feat(anthropic): retry /v1/messages after invalid thinking signature - #25674

Merged
ishaan-berri merged 3 commits into
litellm_ishaan_april14from
litellm_anthropic-messages-thinking-signature-retry
Apr 14, 2026
Merged

feat(anthropic): retry /v1/messages after invalid thinking signature#25674
ishaan-berri merged 3 commits into
litellm_ishaan_april14from
litellm_anthropic-messages-thinking-signature-retry

Conversation

@Sameerlite

@Sameerlite Sameerlite commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Summary

When Anthropic returns an invalid encrypted thinking signature error (common after switching API keys, model deployment, or provider), LiteLLM now strips thinking / redacted_thinking blocks and the top-level thinking param from the outbound Messages API body and retries the HTTP request once.

Implementation

  • Helpers in common_utils.py: detect the error shape and strip thinking content from the request dict.
  • BaseAnthropicMessagesConfig: hooks for max attempts, should-retry, and transform (lazy imports avoid circular imports).
  • async_anthropic_messages_handler: retry loop with re-sign_request for Bedrock.

Coverage

Applies to any provider using BaseAnthropicMessagesConfig (direct Anthropic, Bedrock Claude messages invoke path, Vertex partner Claude, Azure AI Anthropic).

Tests

tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py::TestAnthropicThinkingSignatureSelfHeal

image

Strip thinking blocks from the request body and retry once when Anthropic returns an invalid thinking signature error (e.g. after credential or deployment change). Applies to all BaseAnthropicMessagesConfig providers (direct Anthropic, Bedrock, Vertex, Azure AI).

Made-with: Cursor
@vercel

vercel Bot commented Apr 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 14, 2026 4:34am

Request Review

@codspeed-hq

codspeed-hq Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_anthropic-messages-thinking-signature-retry (5670f6c) with main (e64d98f)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a self-healing retry mechanism for the Anthropic /v1/messages path: when a 400 "invalid thinking signature" error is received (common after rotating API keys or switching model endpoints), LiteLLM now strips thinking/redacted_thinking blocks from the request history and re-signs and retries once. The previous threads' concerns about content: [] and the missing status-code guard have both been resolved in this version.

Confidence Score: 5/5

  • Safe to merge; all remaining findings are non-blocking P2 style suggestions.
  • The two P1 issues from prior review rounds (empty content arrays, missing status-code guard) are both fully addressed. The three remaining comments are minor — a redundant error check, two identical dict copies, and an edge case where a thinking-only assistant turn drops and leaves consecutive same-role user messages. None of these block correct operation for the common case.
  • No files require special attention.

Important Files Changed

Filename Overview
litellm/llms/anthropic/common_utils.py Adds three helpers: error detector, message stripper (correctly drops messages that become empty after stripping), and request dict mutator. Logic is sound; the message-drop behavior when ALL messages contain only thinking blocks is documented but can leave messages: [].
litellm/llms/base_llm/anthropic_messages/transformation.py Adds three hook methods to BaseAnthropicMessagesConfig: max attempts property (2), status-code-guarded should_retry, and transform_anthropic_messages_request_on_http_error. The transform method redundantly re-checks the error type that should_retry already confirmed.
litellm/llms/custom_httpx/llm_http_handler.py Extracts the HTTP POST into a new private retry helper _async_post_anthropic_messages_with_http_error_retry. Loop structure, error handling, re-signing on retry, and the unreachable sentinel RuntimeError are all correct. Two identical dict(litellm_params) copies are created unnecessarily.
tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py Six new pure-mock unit tests covering the detector, stripper, and config hooks — no real network calls. Tests confirm the messages == [] edge case but don't cover the retry loop itself or the consecutive same-role message scenario.

Sequence Diagram

sequenceDiagram
    participant Caller as async_anthropic_messages_handler
    participant Retry as _async_post_anthropic_messages_with_http_error_retry
    participant HTTP as AsyncHTTPHandler
    participant Config as BaseAnthropicMessagesConfig

    Caller->>Retry: POST /v1/messages (with thinking blocks + signed body)
    Retry->>HTTP: post(url, headers, signed_json_body or json body, stream)
    HTTP-->>Retry: 400 HTTPStatusError (invalid thinking signature)
    Retry->>Config: should_retry_anthropic_messages_on_http_error(e, litellm_params)
    Config-->>Retry: "True (status==400 and error matches)"
    Retry->>Config: transform_anthropic_messages_request_on_http_error(e, request_body)
    Note over Config: strips thinking/redacted_thinking blocks<br/>removes top-level "thinking" param
    Config-->>Retry: mutated request_body
    Retry->>Config: sign_request(headers, optional_params, request_body, ...)
    Config-->>Retry: new headers + signed_json_body
    Retry->>HTTP: post(url, new_headers, new_signed_body, stream)
    HTTP-->>Retry: 200 OK response
    Retry-->>Caller: httpx.Response
Loading

Reviews (2): Last reviewed commit: "fix(anthropic): tighten thinking-signatu..." | Re-trigger Greptile

Comment thread litellm/llms/anthropic/common_utils.py
Comment thread litellm/llms/base_llm/anthropic_messages/transformation.py Outdated
Comment thread litellm/llms/custom_httpx/llm_http_handler.py Outdated
Comment thread litellm/llms/base_llm/anthropic_messages/transformation.py Dismissed
Comment thread litellm/llms/base_llm/anthropic_messages/transformation.py Dismissed
- Omit messages whose list content is empty after stripping thinking blocks
- Retry only on HTTP 400 plus invalid-signature body match
- Return response inline from retry loop; drop unreachable None guard
- Tests: thinking-only turn dropped, non-400 no retry

Made-with: Cursor
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 14, 2026 04:33 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 14, 2026 04:33 — with GitHub Actions Inactive
@Sameerlite
Sameerlite temporarily deployed to integration-postgres April 14, 2026 04:33 — with GitHub Actions Inactive
@ishaan-berri
ishaan-berri changed the base branch from main to litellm_ishaan_april14 April 14, 2026 17:10
@ishaan-berri
ishaan-berri merged commit 693c846 into litellm_ishaan_april14 Apr 14, 2026
101 of 108 checks passed
@ishaan-berri
ishaan-berri deleted the litellm_anthropic-messages-thinking-signature-retry branch April 14, 2026 17:10
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…ges-thinking-signature-retry

feat(anthropic): retry /v1/messages after invalid thinking signature
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.

3 participants