hermes - ✅(Solved) Fix DeepSeek V4 /anthropic: signed thinking blocks stripped, causing HTTP 400 [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#17992Fetched 2026-05-01 05:54:34
View on GitHub
Comments
2
Participants
2
Timeline
7
Reactions
0
Timeline (top)
labeled ×4commented ×2cross-referenced ×1

When using DeepSeek v4-pro via the /anthropic endpoint with thinking mode enabled, multi-turn tool calls fail with:

HTTP 400: The `content[].thinking` in the thinking mode must be passed back to the API.

Error Message

Why the existing error classifier doesn't catch it

The thinking_signature error classifier at agent/error_classifier.py:431 requires BOTH "signature" AND "thinking" in the error message: But DeepSeek's error message contains "thinking" without "signature": 3. On the second API call (after tool results come back), the error occurs

  • #17991 — preserve all thinking blocks (only strip redacted_thinking data payloads), and add error classifier pattern for DeepSeek's specific error message

Root Cause

In agent/anthropic_adapter.py:convert_messages_to_anthropic(), the _preserve_unsigned_thinking branch (which covers DeepSeek /anthropic and Kimi /coding) strips any thinking block with a truthy signature field:

if b.get("signature") or b.get("data"):
    # Anthropic-signed block — upstream can't validate, strip
    continue

The assumption is that only Anthropic signs thinking blocks, and third-party endpoints can't validate those signatures. However, DeepSeek v4 may sign its own thinking blocks with a DeepSeek-specific signature. When these signed blocks are stripped, DeepSeek rejects the next request because thinking blocks are required to round-trip.

PR fix notes

PR #18218: fix: preserve DeepSeek V4 signed thinking blocks on /anthropic endpoint

Description (problem / solution / changelog)

Summary

DeepSeek V4 with the /anthropic endpoint signs its own thinking blocks with a signature field. The current _preserve_unsigned_thinking path in the Anthropic adapter strips all blocks that carry a signature or data field, which was designed for Anthropic's proprietary redacted_thinking payloads but now incorrectly strips DeepSeek's legitimate signed thinking blocks too. DeepSeek then rejects the next request with HTTP 400 because thinking blocks must round-trip.

Additionally, the error classifier only catches the thinking_signature failover reason when both "signature" and "thinking" appear in the error message. DeepSeek's actual error ("The content[].thinking in the thinking mode must be passed back to the API.") contains "thinking" but not "signature", so the appropriate failover/retry logic never triggers.

Changes

agent/anthropic_adapter.py

  • Restrict the stripping logic to only remove Anthropic's redacted_thinking blocks (which carry opaque data payloads) and signed blocks that are not normal thinking type. This preserves DeepSeek V4's own signed thinking blocks (which have signature but type="thinking").

agent/error_classifier.py

  • Add a second pattern match for DeepSeek's specific error message format (status 400 + "thinking" + "must be passed back"), mapping it to FailoverReason.thinking_signature so the existing retry/failover path handles it correctly.

Testing

  • All 262 existing tests in test_anthropic_adapter.py and test_error_classifier.py pass.
  • py_compile and ruff check clean for both files.

Closes #17992

Changed files

  • agent/anthropic_adapter.py (modified, +5/-2)
  • agent/error_classifier.py (modified, +13/-0)

Code Example

HTTP 400: The `content[].thinking` in the thinking mode must be passed back to the API.

---

if b.get("signature") or b.get("data"):
    # Anthropic-signed block — upstream can't validate, strip
    continue

---

if status_code == 400 and "signature" in error_msg and "thinking" in error_msg:

---

The `content[].thinking` in the thinking mode must be passed back to the API.
RAW_BUFFERClick to expand / collapse

Description

When using DeepSeek v4-pro via the /anthropic endpoint with thinking mode enabled, multi-turn tool calls fail with:

HTTP 400: The `content[].thinking` in the thinking mode must be passed back to the API.

Root Cause

In agent/anthropic_adapter.py:convert_messages_to_anthropic(), the _preserve_unsigned_thinking branch (which covers DeepSeek /anthropic and Kimi /coding) strips any thinking block with a truthy signature field:

if b.get("signature") or b.get("data"):
    # Anthropic-signed block — upstream can't validate, strip
    continue

The assumption is that only Anthropic signs thinking blocks, and third-party endpoints can't validate those signatures. However, DeepSeek v4 may sign its own thinking blocks with a DeepSeek-specific signature. When these signed blocks are stripped, DeepSeek rejects the next request because thinking blocks are required to round-trip.

Why the existing error classifier doesn't catch it

The thinking_signature error classifier at agent/error_classifier.py:431 requires BOTH "signature" AND "thinking" in the error message:

if status_code == 400 and "signature" in error_msg and "thinking" in error_msg:

But DeepSeek's error message contains "thinking" without "signature":

The `content[].thinking` in the thinking mode must be passed back to the API.

Steps to reproduce

  1. Configure DeepSeek v4-pro with base_url: https://api.deepseek.com/anthropic and thinking mode enabled
  2. Start a conversation that triggers tool calls
  3. On the second API call (after tool results come back), the error occurs

PR

  • #17991 — preserve all thinking blocks (only strip redacted_thinking data payloads), and add error classifier pattern for DeepSeek's specific error message

Environment

extent analysis

TL;DR

Preserve all thinking blocks and update the error classifier to catch DeepSeek's specific error message to fix the multi-turn tool call failure.

Guidance

  • Review the agent/anthropic_adapter.py file and update the convert_messages_to_anthropic() function to preserve thinking blocks with DeepSeek-specific signatures.
  • Update the error classifier in agent/error_classifier.py to catch DeepSeek's error message by modifying the condition to only require the presence of "thinking" in the error message.
  • Test the changes with the steps to reproduce provided in the issue to verify the fix.
  • Consider applying the changes from PR #17991 to preserve all thinking blocks and update the error classifier.

Example

# Updated convert_messages_to_anthropic() function
if b.get("signature") and not b.get("data"):  # only strip Anthropic-signed blocks without data
    continue

Notes

The provided fix assumes that the issue is specific to DeepSeek v4-pro and may not apply to other providers or models. Additionally, the updated error classifier may need to be adjusted if DeepSeek's error message format changes.

Recommendation

Apply the workaround by preserving all thinking blocks and updating the error classifier, as the root cause is identified and a clear fix is available.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix DeepSeek V4 /anthropic: signed thinking blocks stripped, causing HTTP 400 [1 pull requests, 2 comments, 2 participants]