返回指南
故障排查·2026年9月23日·阅读约 6 分钟

Claude API 400“thinking 块中的 `signature` 无效”——是什么破坏了签名,以及如何恢复

API 检查了您发回的 thinking 块,但签名验证失败:签名被截断、修改或以空值发回,或者该块从未由 Claude 签名——又或者,在 Claude Fable 5.1 和 Claude Opus 5.5 上,对话中更早的内容发生了变化。每次重新发送相同历史都会以同样方式失败。找出破坏签名的原因,然后从该对话中一次性移除 thinking 块并继续——您会失去模型先前的推理,但不会失去对话。

最后审核于 。

API 检查了您发回的 thinking 块,但签名验证失败:签名被截断、修改或以空值发回,或者该块从未由 Claude 签名——又或者,在 Claude Fable 5.1 和 Claude Opus 5.5 上,对话中更早的内容发生了变化。每次重新发送相同历史都会以同样方式失败。找出破坏签名的原因,然后从该对话中一次性移除 thinking 块并继续——您会失去模型先前的推理,但不会失去对话。

错误

响应(HTTP 400)
// Through Kunavo: the upstream message as it reaches you, typed as Anthropic
// types a 400; no request_id field, the upstream's own id is appended instead:
{"type":"error","error":{"type":"invalid_request_error","message":"messages.1.content.0: Invalid `signature` in `thinking` block (request id: …)"}}
// (the message path, any masking of it and the appended request id vary by upstream)

// From Anthropic's API directly:
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "messages.1.content.0: Invalid `signature` in `thinking` block"
  },
  "request_id": "req_011C..."
}

// messages.{i}.content.{j}: i = position in messages[], j = block index. Both vary.
// An upstream can mask that path (***.***) and append its own request id, as above.
// Claude Code prints the body after "API Error: 400".
// On Claude Fable 5.1 and Claude Opus 5.5 the message can continue:
//   "... The block is bound to a different conversation. Remove the block, or set
//    `thinking.block_binding.prefix_mismatch_behavior` to "drop_block"."

原因和解决方法一览

原因解决方法
签名在返回前被截断、清空或编辑按照返回时的原样准确存储并重放每个块。让 SDK 组装流式轮次,避免丢失 signature_delta。
该块从未由 Claude 签名Anthropic 兼容 URL 背后的非 Claude 模型,或自行写入签名的代理。将这些轮次作为纯文本和 tool_use 发回。
您在对话中途切换了基础 URL、账户或登录方式Claude Code 2.1.152+ 会在切换模型或登录方式后移除过期签名。在您自己的代码中,如果切换后的第一个请求失败,请一次性移除 thinking。
“该块绑定到了不同的对话”(Fable 5.1、Opus 5.5)系统提示、工具或更早的消息发生了变化。保持历史只追加不修改,或者选择 drop_block(需要 beta 标头)。
代理或网关在传输过程中重写了历史它的重写会被视为您的编辑。直接针对 API 重现请求,以判断是否由此导致。

查看哪项检查失败

错误措辞会告诉您原因。如果消息停留在“thinking 块中的 `signature` 无效”,说明签名本身未通过验证:Anthropic 列出的原因包括签名被截断、修改或以空值发回(https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting,截至 2026 年 9 月),其 preserved-thinking 页面称这是被篡改或无法解密的签名,并且始终返回 400。被掩码为 ***.***.content.0 的路径,或网关追加的请求 ID,都不会改变这一点;关键在于后面是否还有关于对话的句子。在 Claude Fable 5.1 和 Claude Opus 5.5 上,相同措辞可能继续显示“该块绑定到了不同的对话”——这是另一项检查,在最后一步介绍。第三种消息“最新 assistant 消息中的块不能被修改”表示最新 assistant 轮次被编辑、过滤、重新排序或重建;编辑 thinking 文本会产生该错误,而不是签名错误。重试相同请求体无法清除其中任何一种错误。

three thinking-block 400s
Invalid `signature` in `thinking` block
  -> the signature did not verify: truncated, altered, empty, or not Claude's

Invalid `signature` in `thinking` block. The block is bound to a different conversation. ...
  -> Fable 5.1 / Opus 5.5: system, tools or an earlier message changed after the block was made

`thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified
  -> the newest assistant turn was edited, filtered, reordered or rebuilt before it was sent back

按返回原样发回 assistant 轮次

每个 thinking 块都带有签名——完整推理的加密副本——API 使用它来验证该块由 Claude 生成(https://platform.claude.com/docs/en/build-with-claude/thinking#thinking-encryption)。原样追加响应的内容列表:thinking、redacted_thinking 和 tool_use 块,包括文本为空的 thinking 块;在较新的模型上,这是默认显示方式。使用流式传输时,签名会在块关闭前通过单个 signature_delta 到达,因此手写的累加器如果漏掉它,就会存储空签名,带空签名发回的块会失败;Anthropic 建议让 SDK 组装消息。JSON 键的顺序和空白不重要——值才重要。

replay_verbatim.py
import anthropic

client = anthropic.Anthropic(base_url="https://api.kunavo.com", api_key="sk-kn-...")

tools = [{
    "name": "get_weather",
    "description": "Current weather for a city.",
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]
messages = [{"role": "user", "content": "What's the weather in Paris?"}]

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=16000,
    thinking={"type": "adaptive"},
    tools=tools,
    messages=messages,
) as stream:
    final = stream.get_final_message()   # signature_delta already applied

# Append the content list untouched: thinking, redacted_thinking, tool_use.
messages.append({"role": "assistant", "content": final.content})

# Not this: a store that keeps the text but not the signature replays
# {"type": "thinking", "thinking": "...", "signature": ""}  -> this 400.

不要将其他后端的 thinking 放入 Claude 历史

根据 Anthropic 的文档,在其自身 API 上切换 Claude 模型不应触发此错误:文档要求您在切换时继续发送这些块,无法读取的块会被新模型无错误地丢弃,并说明签名可在 Claude API、Amazon Bedrock 和 Google Cloud 之间移植(https://platform.claude.com/docs/en/build-with-claude/thinking,截至 2026 年 9 月)。但它无法验证从未由 Claude 签名的块。公开报告涉及经过其他后端的历史:一个先在 GLM 后端运行、再返回 Anthropic 的 Claude Code 会话(github.com/anthropics/claude-code/issues/21726);一个代理将 Gemini 轮次伪装成带有自身签名的 Claude thinking 块(github.com/router-for-me/CLIProxyAPI/issues/1584);以及一个在会话中途切换到另一把密钥、再切回的 Claude Code 会话(github.com/lbjlaq/Antigravity-Manager/issues/388)。对于非 Claude 模型生成的轮次,Anthropic 的建议是仅将该模型的输出作为文本和 tool_use 内容发回。

foreign_turn.py
def as_foreign_turn(content: list[dict]) -> list[dict]:
    """A turn a non-Claude model produced: keep what it said and did,
    never its thinking blocks, which Claude cannot verify."""
    return [b for b in content if b["type"] in ("text", "tool_use")]

恢复已经失败的对话

从已存储的历史中一次性移除 thinking 和 redacted_thinking 块——全部移除最简单;对于“绑定到了不同的对话”这一变体,Anthropic 明确的最低要求是移除指定块及其之后的每一个块——保留其他所有块的原有位置,保存该历史并继续。Anthropic 将此作为无法再重放的已保存会话的恢复方法(https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#faq);如果签名无法验证,唯一的其他方法是按返回时的原样重放该块,前提是您仍然保留它。Claude Code 在 API 拒绝签名时会自行移除较早的 thinking。移除这些块并继续后,不要再放回它们:在 Fable 5.1 上,被移除后又放回的块会使其缺失期间产生的 thinking 失效。模型会在没有先前推理的情况下回答,从这里开始产生的新 thinking 有效。在 Claude Code 中,2.1.152(2026 年 5 月 27 日,https://code.claude.com/docs/en/changelog)会在切换模型或登录身份后移除过期签名,其网关指南说明它会在没有较早 thinking 块的情况下重试签名拒绝——但该重试依赖上游错误措辞匹配,而用自身信封包装错误的网关可能使其失效(https://code.claude.com/docs/en/llm-gateway-protocol#automatic-retry-and-error-forwarding)。

strip_thinking.py
THINKING = {"thinking", "redacted_thinking"}

def block_type(b) -> str:
    return b["type"] if isinstance(b, dict) else b.type   # dicts or SDK objects

def strip_thinking(messages: list[dict]) -> list[dict]:
    """One-time recovery: drop every thinking block, keep everything else."""
    out = []
    for m in messages:
        content = m["content"]
        if m["role"] == "assistant" and isinstance(content, list):
            kept = [b for b in content if block_type(b) not in THINKING]
            content = kept or [{"type": "text", "text": "(no visible reply)"}]  # keep the turn non-empty
        out.append({**m, "content": content})
    return out

messages = strip_thinking(messages)   # save this version; never re-add the blocks

在 Fable 5.1 上固定前缀——或选择 drop_block

“Bound to a different conversation”(“绑定到了不同的对话”)是 preserved-thinking 检查:在 Claude Fable 5.1 和 Claude Opus 5.5 上,重放的块只有在系统提示、工具和所有更早消息均未改变时才有效。对于 2026 年 8 月 31 日或之后创建的账户,Anthropic 默认强制执行该规则(https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement);通过网关时,该账户并不属于您,因此应假设该规则已启用。在会话期间保持系统提示和工具固定,只追加,不编辑。为了在查找导致问题的编辑时保持请求成功,请发送 thinking-binding-controls-2026-08-01 beta 标头,并将 prefix_mismatch_behavior 设置为 drop_block;没有该标头时,该字段本身会被拒绝,并显示“block_binding: Extra inputs are not permitted”(https://platform.claude.com/docs/en/api/errors)。

drop_block.sh
# Anthropic's API directly. The field needs the beta header; Kunavo forwards it (see below).
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: thinking-binding-controls-2026-08-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-fable-5-1",
    "max_tokens": 16000,
    "thinking": {
      "type": "adaptive",
      "block_binding": {"prefix_mismatch_behavior": "drop_block"}
    },
    "messages": [{"role": "user", "content": "..."}]
  }'

如果你通过 Kunavo 调用

Kunavo 会按发送时的原样通过 /v1/messages 转发 thinking 和 redacted_thinking 块,包括签名和数据——唯一的请求更改是模型 ID,以及在拒绝这些参数的模型上移除 temperature、top_p 和 top_k——并按上游发送的方式返回响应正文,无论是否为流式响应。截至 2026 年 9 月,每个 Claude 模型都通过一个上游通道提供服务,没有备用通道,因此 Kunavo 的路由不会在提供商之间移动对话,400 错误也绝不会在其他地方重试。我们尚未测试在 Anthropic 直连与 Kunavo 之间移动对话,因此预计切换后的第一个请求需要移除 thinking。自 2026 年 9 月 24 日起,Kunavo 会转发 thinking-binding-controls-2026-08-01 beta;当天的一项测试中,为 Claude Fable 5.1 提供服务的通道在有或没有该标头的情况下都接受 block_binding,我们尚未观察到它丢弃块。拒绝会以 HTTP 400、类型为 invalid_request_error 的形式返回,不包含 request_id 字段,并携带上游的消息文本——该文本可能掩盖 messages.N 路径,并以上游自己的请求 ID 结尾——因此请根据状态码和“Invalid `signature` in `thinking` block”这几个词进行匹配。Claude Code 的自动移除并重试功能依赖该措辞:Claude Code 2.1.280 指向一个以此信封响应的测试服务器时,移除了 thinking 块并重试。我们尚未通过 Kunavo 本身触发该错误,因此如果会话仍然在每一轮都失败,请开始新会话。失败的请求不收费。 原生端点原样转发的内容列于 Messages API 参考.

常见问题

“thinking 块中的 `signature` 无效”是什么意思?

API 无法验证您发回的 thinking 块。每个 thinking 块都带有签名——Claude 推理的加密副本——当签名被截断、修改或以空值发回,或者该块从未由 Claude 签名时,验证就会失败。这是 400 错误,而不是暂时性错误:相同请求每次都会失败。

thinking 块签名会过期吗?

Anthropic 的文档没有提到过期。在 anthropic-sdk-python 跟踪器(问题 #1598,2026 年 8 月)中,一位 GitHub 标记为贡献者的账户回复称签名不会过期,并表示当到达 API 的块与返回的块不一致时检查会失败——但这是一条问题评论,而不是文档。如果以前能够正常工作的已保存会话现在失败,请检查哪些因素可能改变了已存储的块或它们经过的路由:存储层、代理或后端切换。

我可以直接删除 thinking 块并继续吗?

可以。Anthropic 将其作为无法重放的已保存会话的恢复方法,Claude Code 在签名被拒绝时也会自行移除较早的 thinking。移除 thinking 和 redacted_thinking 块——全部移除最简单——保留其他块,然后重试一次。模型会失去先前的推理,但不会失去对话;对于不涉及工具使用的情况,Anthropic 文档也允许省略较早轮次的 thinking。

为什么在切换模型或提供商后会发生?

Anthropic 文档称,在其 API 上切换 Claude 模型时,新模型无法读取的块会被丢弃且不会报错,并且签名可跨 Claude API、Amazon Bedrock 和 Google Cloud 使用。Claude Code 仍然需要修复在模型或登录方式切换后因过期签名而卡住的会话(2.1.152);公开报告涉及经过 Claude 无法验证的内容——同一基础 URL 背后的非 Claude 模型、写入自身签名的代理,或丢失签名的客户端。切换后一次性移除 thinking 块。

Claude Code 会自动修复吗?

较新版本会尝试修复。自 2.1.152 起,它会在模型或登录方式切换后移除过期签名,并在不带较早 thinking 块的情况下重试签名拒绝。该重试依赖上游错误措辞匹配,而 Anthropic 的网关指南指出,用自身信封包装错误的网关可能使其失效。请先运行 claude update;如果某个会话仍然在每一轮都失败,请开始新会话。

相关指南

更多错误语义请参阅 错误参考;通过 注册 和 身份验证指南 获取密钥只需一分钟。