Claude API 的报错、限流和停止原因:每个状态码和 stop_reason 是什么意思,通过中转站时哪些不一样
调 Claude 的接口时,请求没成功是一回事,成功了但回答停得不对是另一回事。本文整理官方的十一个报错状态码、两种限额、七个停止原因和拒答的处理办法,并通过中转站用 Python SDK 把能触发的都触发了一遍:报错的类型名、限流的响应头、停止序列等好几处和官方文档不一样。
本站已实测,测试日期:2026-10-13。
本页目录(11 节)
这篇教程解决一个问题:调 Claude 的接口,或者 Claude Code 替你调的时候,请求没成功、或者回答停在了半截,怎么从返回的东西里看出原因,下一步该做什么?
Claude Code 里看到的 API Error: 429、529 Overloaded 都出自这一套,所以不自己写代码的读者也用得上前半部分。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-13 |
| 版本 | Anthropic Python SDK 1.13.0,Python 3.12.3 |
| 模型 | Sonnet 5.5,个别地方用 Opus 5.5 对照 |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
先说清楚这次实测的边界: 本站没有官方的 key,官方接口本身没有测。下面每一节先讲官方文档的规则,凡是写“实测”的,都是通过中转站看到的。两边不一样的地方集中在第七节。
一、先分清两件事
| 报错 | 停止原因 | |
|---|---|---|
| HTTP 状态码 | 4xx 或 5xx | 200 |
| 意思 | 请求没处理成 | 请求处理成了,这是回答为什么在这里停下 |
| 在哪看 | 响应体里的 error |
响应体里的 stop_reason |
| 有没有内容 | 没有 | 有,可能不完整 |
很多“回答被截断”“工具没调完”的问题不是报错,是没看 stop_reason。
二、报错:十一个状态码
官方接口的报错都是这个形状:
{
"type": "error",
"error": { "type": "not_found_error", "message": "The requested resource could not be found." },
"request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}
| 状态码 | error.type |
意思 | 怎么办 |
|---|---|---|---|
| 400 | invalid_request_error |
请求的格式或内容有问题。用量到了你自己设的花费上限也是 400 | 看 message,改请求 |
| 401 | authentication_error |
key 有问题:写错、被撤销、过期 | 换 key |
| 402 | billing_error |
账单或付款信息有问题 | 去控制台看付款 |
| 403 | permission_error |
这把 key 没有权限用这个资源 | 看组织和工作区的设置 |
| 404 | not_found_error |
资源不存在 | 检查路径和 ID |
| 409 | conflict_error |
和资源当前的状态冲突 | 解决冲突后重试 |
| 413 | request_too_large |
请求体太大。Messages API 的上限是 32 MB | 减小请求 |
| 429 | rate_limit_error |
触发了限流,或者到了这一档的月度花费上限 | 见第四节 |
| 500 | api_error |
Anthropic 内部出错 | 退避重试,一直不好就带上请求编号找支持 |
| 504 | timeout_error |
处理超时 | 长请求改用流式 |
| 529 | overloaded_error |
接口整体过载,所有用户都受影响 | 等一会儿重试 |
429 和 529 容易混:429 是你自己的额度到了,529 是大家的服务挤了。
每个响应都带一个请求编号,在 request-id 响应头里,报错时响应体里也有。找官方支持时要给这个。
文档还单独列了几种常见的 400,都和模型换代有关:
| 你发了什么 | 现在的结果 |
|---|---|
| 最后一条消息是 assistant(预填回答的开头) | Claude 4.6 及之后的模型不支持,400 |
thinking 的 type 是 enabled |
Claude 4.7 及之后的模型不支持,改用 adaptive 加 effort |
把 thinking 设成 disabled |
Opus 5.5 等模型思考永远开着,400。Sonnet 5.5 要改用 between_tools |
| 把上一轮的 thinking 块改过、删过再发回去 | 400。带工具调用时,thinking 块要原样传回 |
三、SDK 怎么把报错交给你
用官方 SDK 时,报错不是一段 JSON,是一个异常,类名按状态码分:
import anthropic
client = anthropic.Anthropic() # 地址和 key 从环境变量里取
try:
msg = client.messages.create(
model="claude-sonnet-5-5", max_tokens=1024,
messages=[{"role": "user", "content": "你好"}],
)
except anthropic.APIStatusError as e: # 所有带状态码的报错的父类
print(type(e).__name__, e.status_code)
print(e.body) # 响应体里的 JSON
实测触发了三种:
| 怎么触发的 | 异常类 | 状态码 |
|---|---|---|
| 模型名写错 | NotFoundError |
404 |
messages 是空列表 |
BadRequestError |
400 |
| key 是错的 | AuthenticationError |
401 |
SDK 还替你做了三件事:
- 自动重试。 连接错误、限流和 5xx 会按指数退避重试,响应里有
retry-after就照它等。实测这个版本默认重试 2 次,用max_retries改,设成 0 就是不重试。 - 超时。 实测默认的读超时是 600 秒。
- 拦下太长的非流式请求。 非流式请求的
max_tokens设到 128000,SDK 根本不发出去,直接抛异常:
ValueError:Streaming is required for operations that may take longer than 10 minutes.
原因是有些网络会掐掉长时间没有数据的连接。不需要逐段处理输出的话,可以让 SDK 替你把流读完,拿到的还是一个完整的消息对象:
with client.messages.stream(model="claude-sonnet-5-5", max_tokens=128000, messages=[...]) as stream:
message = stream.get_final_message()
流式响应有一处特别:报错可能在已经返回 200 之后才来,以事件的形式出现在流里,不走上面的状态码那一套。
四、限额:两种,表现不一样
| 花费上限 | 速率限制 | |
|---|---|---|
| 限的是什么 | 一个月最多花多少钱 | 一段时间里最多发多少请求、多少 token |
| 到了之后 | 一直失败,到下个月 1 号 UTC 零点或者你升档 | 等一会儿就恢复 |
retry-after 头 |
没有 | 有 |
两种到了都是 429,类型都是 rate_limit_error。区分的办法:花费上限的那种,响应体里多一项:
"details": { "error_code": "enforced_spend_limit_reached" }
这种 429 重试没有用,SDK 的自动重试也一样失败。先看有没有 retry-after,没有就别重试了。
花费上限
组织按使用历史自动分档,每一档有月度上限:
| 档位 | 每月花费上限 |
|---|---|
| Start | 500 美元 |
| Build | 1,000 美元 |
| Scale | 200,000 美元 |
| Custom | 没有,和客户团队商定 |
新组织可能先在一个额度更低的评估档里待一段时间。
你也可以在控制台给自己设一个更低的上限。碰到自己设的上限时返回的是 400,不是 429,消息以 You have reached your specified API usage limits 开头。
速率限制
按模型分别算,量三样:每分钟请求数(RPM)、每分钟输入 token(ITPM)、每分钟输出 token(OTPM)。Sonnet 5.5 的标准额度:
| 档位 | RPM | ITPM | OTPM |
|---|---|---|---|
| Start | 1,000 | 2,000,000 | 400,000 |
| Build | 5,000 | 5,000,000 | 1,000,000 |
| Scale | 10,000 | 10,000,000 | 2,000,000 |
几条不看文档想不到的规则:
- 用的是令牌桶。 额度持续回填,不是整点清零。每分钟 60 次可能被执行成每秒 1 次,短时间里猛发一阵照样会触发。
- 从缓存读的输入不算进 ITPM。 对多数模型,只有没缓存的输入和写缓存的输入才算。缓存命中率 80% 时,同样的额度能处理五倍的输入。这是用提示缓存的另一个理由。
- OTPM 只算真正生成的 token。
max_tokens设多大不影响限流。 - 用量突然猛增也会 429。 文档管这叫加速限制,办法是慢慢加量。
每个响应都带着一组头,告诉你现在的余量:
| 响应头 | 内容 |
|---|---|
retry-after |
要等几秒才能重试 |
anthropic-ratelimit-requests-remaining |
还能发几次请求 |
anthropic-ratelimit-input-tokens-remaining |
还剩多少输入 token |
anthropic-ratelimit-output-tokens-remaining |
还剩多少输出 token |
每一种还各有 -limit(上限)和 -reset(什么时候回满)两个头。
服务等级
请求有三种等级:标准(默认)、优先(Priority Tier)、批量。优先等级的容量承诺已经停止新购,只有已经签了合同的组织还在用。每个响应的 usage 里有一项 service_tier,写着这次请求走的是哪一种。实测拿到的是 standard。
五、停止原因:七个值
每个成功的响应都有 stop_reason:
| 值 | 什么时候出现 | 怎么办 |
|---|---|---|
end_turn |
它自己说完了 | 直接用 |
max_tokens |
到了你设的 max_tokens |
调大,或者接着生成 |
stop_sequence |
输出里出现了你指定的停止序列 | 看 stop_sequence 字段是哪一个 |
tool_use |
它要调工具 | 执行工具,把结果传回去 |
pause_turn |
服务端工具(比如网页搜索)的循环到了次数上限,默认 10 次 | 把这条回复原样传回去,让它接着做 |
refusal |
它拒绝回答 | 见第六节 |
model_context_window_exceeded |
回答把模型的上下文窗口占满了 | 当成被截断的回答处理 |
实测触发了前面几个里的三个:
--- max_tokens=5
stop_reason=max_tokens 内容块=['text'] 文字='长城是中国' output_tokens=5
--- 带一个工具,问“上海现在天气怎么样?”
stop_reason=tool_use 内容块=['tool_use']
--- 普通的一问一答
stop_reason=end_turn
max_tokens 那次,回答停在了“长城是中国”五个字上,没有任何别的提示。不检查 stop_reason,你拿到的就是半句话。
流式时它出现的位置是固定的。实测的事件顺序:
message_start:stop_reason = None
message_delta:{"container": null, "stop_details": null, "stop_reason": "max_tokens", "stop_sequence": null}
事件顺序:message_start → content_block_start → content_block_delta → text → content_block_stop → message_delta → message_stop
开头的 message_start 里它是空的,要等到快结束的 message_delta 才有。顺序里的 text 是 SDK 自己加的便利事件,其余是接口发来的。
文档里另外几条值得记的:
- 被
max_tokens截断的回答如果停在一个不完整的工具调用上,只能调大max_tokens重发。 - 偶尔会拿到一个空的
end_turn。 常见的原因是你在工具结果后面紧跟着加了一段文字,或者把它已经说完的回答原样又发了一遍。办法是工具结果后面不要加文字;确实要催它,另起一条用户消息。 tool_use之后的那条用户消息里,只能放工具结果。
六、拒答:它是 200,不是报错
Fable 5.1、Fable 5、Opus 5.5、Opus 5、Sonnet 5.5、Haiku 5.5 这几个模型带着安全分类器,可能拒绝一个请求。拒绝时你拿到的是一个正常的 200 响应:
{
"content": [],
"stop_reason": "refusal",
"stop_details": {
"type": "refusal",
"category": "cyber",
"explanation": "This request was declined because it could enable cyber harm."
}
}
category 说的是哪一类政策,五个取值里有三个即使一个字没输出也要收费:
category |
意思 | 没输出时收不收费 |
|---|---|---|
cyber |
可能助长网络攻击。正当的安全工作也可能触发 | 不收 |
bio |
可能助长生物危害。正当的生命科学工作也可能触发 | 收 |
frontier_llm |
可能帮助开发竞争的 AI 模型。正当的机器学习工作也可能触发 | 收 |
reasoning_extraction |
要求模型把内部的推理过程写进回答里 | 收 |
general_harms |
上面四类之外的使用政策范围 | 不收 |
category 和 explanation 也可能都是空的。写代码时按 stop_reason 判断,不要假定这两个字段一定有值。explanation 的文字不保证稳定,拿来显示可以,别去解析。
拒答可能发生在输出之前,也可能在流式输出到一半时。已经收到的那部分要当成不完整的,丢掉。
被拒了怎么办
多数情况下,同一个请求换一个模型就能答。三种做法:
| 你的情况 | 用什么 |
|---|---|
| 直连 Claude API,要最省事的 | 服务端回退:请求里加 fallbacks: "default",被拒时接口自己换到官方推荐的模型上重试,你只收到一个响应。目前是测试版,要带 beta 头 |
| 用官方 SDK,任何平台 | SDK 的中间件:在客户端配一次,自动重试 |
| 自己发 HTTP,或者要自己控制重试 | 手动重试,配合回退抵扣 |
回退抵扣解决的是一个费用问题:提示缓存是按模型分开的,换了模型,之前缓存的那段对话要重新写一遍缓存,而写比读贵。拒答的响应里带一个抵扣令牌,重试时带上它,就按“这段对话一直在新模型上”来计费。前两种做法已经替你处理了,只有手动重试才需要管。
两个例外:
- Haiku 5.5 没有服务端回退,也没有回退抵扣。 要用 SDK 中间件或者自己重试。
reasoning_extraction没有推荐的回退模型,要改提示词。 它通常是因为提示里要求模型把思考过程写出来:一个要它先填的<thinking>段,JSON 输出里的reasoning字段,诸如此类。这类措辞可能藏在系统提示、skill 或者工具描述里。你仍然可以让它解释答案、给出依据;想看它怎么想的,读 thinking 块。
本站没有去故意触发拒答,这一节是按文档整理的。
七、通过中转站实测到的不一样
这是这次实测最主要的收获。同样的请求,通过中转站拿到的结果和官方文档写的有这些出入:
| 官方文档 | 实测(酷站AI,2026-10-13) | |
|---|---|---|
| 模型名写错 | 404,not_found_error |
404,类型是 model_not_found,消息是中文 |
| key 是错的 | 401,authentication_error |
401,类型是 auth_failed |
| 报错的响应体 | 顶层有 "type": "error" 和 request_id |
有的有,有的没有;有的多一个 code 字段 |
| 请求编号 | request-id 头,req_ 开头 |
没有这个头。有一个 x-request-id,是中转站自己的编号 |
| 限流的响应头 | 每个响应都有一组 anthropic-ratelimit- 头 |
一个也没有 |
不带 max_tokens |
这是必填参数 | 503,带 retry-after: 60,类型是 billing_admission_failed |
stop_sequences |
命中时停下,stop_reason 是 stop_sequence |
不生效。照常输出完,stop_reason 是 end_turn |
| 最后一条是 assistant | 400 | 200,模型接着预填的字往下写了 |
thinking 设成 disabled |
400,提示改用 between_tools |
200,正常回答 |
thinking 设成 between_tools |
Sonnet 5.5 支持 | 400,消息说本站暂不支持,改用 effort |
| 多一个不认识的字段 | 这一组文档没有写 | 200,被忽略 |
不带 anthropic-version 头 |
这一组文档没有写 | 200,照常回答 |
| 路径写错 | JSON 格式的 404 | 纯文本的 404 page not found |
其中三处的原始输出:
--- 模型名写错
异常类:NotFoundError HTTP 404
响应体:{"error": {"message": "模型 \"claude-does-not-exist\" 不存在,请检查模型名称;可在控制台“模型”页查看可用模型。", "type": "model_not_found"}, "type": "error"}
--- 不带 max_tokens,连发两次
HTTP 503 retry-after=60 {"error": {"message": "计费用量记录暂不可用,本次请求未发送到上游", "type": "billing_admission_failed"}, "type": "error"}
HTTP 503 retry-after=60 {"error": {…同上…}}
--- stop_sequences=['3'],让它从 1 数到 5
HTTP 200 stop_reason=end_turn stop_sequence=None 文字='1, 2, 3, 4, 5'
停止序列这一条,Sonnet 5.5 和 Opus 5.5 各试了一次,用 SDK 和直接发 HTTP 各试了一次,中文和英文的序列都试了,结果相同。把 stop_sequences 故意写成错的类型(一个字符串、一个数字列表)也没有报错。
没有出入的部分:状态码本身是对的,所以 SDK 的异常类没有分错;end_turn、max_tokens、tool_use 三个停止原因正常;流式里 stop_reason 出现的位置正常;usage 里的字段齐全;Authorization: Bearer 和 x-api-key 两种带 key 的写法都认。
所以通过中转站写代码时
- 判断出了什么错,看 HTTP 状态码或者 SDK 的异常类,不要看
error.type。 类型名是中转站自己起的。 max_tokens一定要带。 漏了它拿到的是一个带retry-after的 503,SDK 会当成临时故障去重试,重试多少次都一样。- 不要依赖
stop_sequences。 需要在某个标记处截断,拿到完整输出后自己截。 - 拿不到限流的余量。 没有那组响应头,要自己控制发送的速度,遇到 429 自己退避。
- 排查问题时记下
x-request-id。 找中转站查某一次请求,用的是这个编号。 - 在中转站上能跑通的请求,换到官方接口上可能被拒。 预填、
thinking: disabled、多余的字段,官方那边都会是 400。
这些出入本站已经整理出复现步骤。它们是这一家、这一天的表现,别家可能不同,这一家以后也可能改。选中转站时可以照着这张表自己测一遍;辨别中转站的其他办法,见《通过中转站用 Claude Code》。
八、地区和 IP 地址
支持的地区。 官方列了一份可以使用 Claude API 的国家和地区名单。本站查了一下:名单里有日本、新加坡、台湾,没有中国大陆、香港和澳门。
IP 地址。 公司的防火墙要放行时用得上:
| 方向 | 地址段 |
|---|---|
| 你连 Anthropic(IPv4) | 160.79.104.0/23 |
| 你连 Anthropic(IPv6) | 2607:6bc0::/48 |
| Anthropic 连你(比如它去调你的 MCP 服务器) | 160.79.104.0/21 |
和 Claude Code 的关系
Claude Code 就是这套接口的一个客户端,它显示的报错是同一批:
API Error: 429:这把 key 的速率限制或花费上限到了。Repeated 529 Overloaded errors:服务整体繁忙,不算在你的额度里。- 回答突然停了、工具调到一半:多半对应某个
stop_reason,Claude Code 自己会处理多数情况。
在 Claude Code 里怎么一步步查,见《Claude Code 常见报错怎么查》。
花费
这次一共发了四十来次请求,每次都只有几十个 token,花费没有单独统计。
这次实测没有覆盖到的
- 官方接口没有测。 表里“官方文档”一列是照文档写的,本站没有官方的 key 可以对照。
- 429、529、5xx、413、402、403、409 没有触发。 限流和过载要大量请求或者碰运气,本站没有去试。
- 拒答没有触发,服务端回退、SDK 中间件、回退抵扣都是按文档整理的。
pause_turn和model_context_window_exceeded没有触发。- 只测了一家中转站、一天、两个模型。
- TypeScript 等其他语言的 SDK 没有测。