从请求日志入手,排查 Claude API 故障会更靠谱
Claude API 调用失败时,很多人的第一反应都是先看错误提示:比如 400 多半是参数问题,401 可能是 Key 不对,429 是限流,5xx 看起来像服务端异常。这个思路当然没错,但如果放到生产环境里,只看这些往往还不够。

同样是 400 invalid_request_error,背后的原因可能完全不同:有时是 messages 结构不符合要求,有时是 tools 的 schema 写错了,也可能是 max_tokens 超过限制,甚至只是模型名拼错。再比如 timeout,看起来都是“超时”,但可能是客户端自己设置得太短,也可能是代理的 idle timeout、流式响应首包太慢,或者前端 SSE 解析出了问题。
所以,真正有效的 Claude API 排障,不能只停留在“这个错误码是什么意思”。更关键的是回到请求本身:这次请求什么时候发出,用了哪个模型,是否开启 stream,HTTP 状态码是什么,error.type 是什么,有没有 request-id,耗时多久,重试了几次,请求体大概长什么样。
下面就从请求日志的角度,整理一套更适合线上环境的 Claude API 故障排查方法。
一、一条 Claude API 请求日志至少应该记录什么
如果日志里只有一句“Claude API call failed”,基本就没法查。线上排障时,建议至少把下面这些字段记录下来。
| 字段 | 作用 |
|---|---|
timestamp |
用来判断故障发生的时间范围 |
trace_id |
方便关联你自己业务系统里的链路日志 |
request_id |
对应 Claude API 响应 Header 或错误对象里的请求 ID |
endpoint |
判断接口路径有没有写错 |
model |
判断模型名、模型权限,以及模型切换带来的影响 |
status_code |
快速区分 4xx、429、5xx 等问题 |
error.type |
判断错误类型,比如认证失败、限流、请求格式错误 |
error.message |
辅助定位具体字段问题,但要注意脱敏 |
stream |
区分普通响应问题和流式响应问题 |
latency_ms |
判断这次请求整体耗时 |
first_token_latency_ms |
在 stream 场景下判断首包是否过慢 |
retry_count |
判断问题是否被重试放大 |
timeout_ms |
判断客户端超时配置是不是太短 |
input_tokens / output_tokens / usage |
用来分析成本、截断、限流和上下文长度问题 |
request_summary |
记录请求摘要,而不是完整请求体 |
sdk_version / runtime |
判断 SDK、语言运行时或封装层差异 |
proxy / region / instance |
排查网络、代理、区域或实例级问题 |
比较推荐的做法是使用结构化 JSON 日志。这样后面检索、聚合和做监控都会方便很多:
{
"timestamp": "2026-06-22T10:21:33+08:00",
"level": "ERROR",
"trace_id": "biz-trace-8f3a",
"request_id": "req_xxx_redacted",
"provider": "claude_api",
"endpoint": "/v1/messages",
"model": "claude-xxx",
"status_code": 400,
"error": {
"type": "invalid_request_error",
"message": "tools.0.input_schema.properties.xxx.type is invalid"
},
"stream": false,
"latency_ms": 238,
"retry_count": 0,
"timeout_ms": 60000,
"request_summary": {
"messages_count": 6,
"tools_count": 2,
"max_tokens": 1024,
"temperature": 0.3,
"has_system_prompt": true
},
"sdk": {
"language": "nodejs",
"version": "x.x.x"
}
}
这里有个很重要的原则:日志里记录请求摘要,不要记录完整请求内容。
尤其是用户原始输入、系统提示词、文件内容、工具调用参数这些信息,都不应该原样写进日志。排查需要证据,但不能为了排查把敏感信息暴露出去。
二、先看 5 个字段,大致判断问题在哪一层
拿到一条失败日志后,不要急着改代码。更好的做法是先看几个关键字段:
status_codeerror.typerequest_idlatency_ms / first_token_latency_msstream / retry_count
基本上,通过这几个字段,就能先把问题范围缩小一大截。
| 日志特征 | 大概率问题层级 | 排查方向 |
|---|---|---|
没有 request_id,客户端直接抛异常 |
请求可能还没真正发出去 | SDK 初始化、网络、DNS、代理、客户端超时 |
400 + invalid_request_error |
请求构造层 | messages、tools、max_tokens、模型名、content 类型 |
401 + authentication_error |
认证层 | API Key、Authorization Header、环境变量 |
| 403 + 权限类错误 | 权限层 | 账号权限、模型权限、组织或区域限制 |
429 + rate_limit_error |
限流层 | 并发、RPM、TPM、批量任务、重试风暴 |
5xx/529 + 有 request_id |
服务端或上游临时异常 | 有限重试、降级、收集多个 request-id |
| 200 但业务异常 | 响应解析或生成结果问题 | stop_reason、usage、工具调用、客户端解析 |
| stream=true,中途断开 | 流式链路问题 | SSE 解析、代理 idle timeout、客户端 timeout |
这里特别要强调 request_id。它通常来自响应 Header,或者错误对象里也会带。排查时最好把它和业务侧的 trace_id 绑定起来:trace_id 用来查你自己的调用链,request_id 用来定位这一次 Claude API 请求。
如果后续需要向平台或技术支持反馈问题,优先提供 request_id,比只发一张错误截图有用得多。
三、按状态码排查 Claude API 常见错误
1. 400:优先检查请求体结构
400 invalid_request_error 通常是比较确定的错误,也就是说,盲目重试大概率没有意义。你反复发同样的请求,结果通常还是 400。
这类问题重点看下面几项:
model是否拼写正确,具体模型名要以官方或接入平台文档为准;messages结构是否符合接口要求;content类型有没有传错;max_tokens是否设置得不合理;tools或 tool schema 是否满足 JSON Schema 要求;- 是否把接口不支持的字段塞进了请求体;
- SDK 或内部封装层有没有改写请求结构。
日志里可以记录这类摘要信息:
"request_summary": {
"messages_count": 8,
"tools_count": 3,
"max_tokens": 4096,
"content_types": ["text", "image"],
"tool_names": ["search_order", "query_user"]
}
这样既能帮助定位问题,又不会把完整 messages 暴露出来。一般来说,记录消息数量、content 类型、工具数量、工具名称、参数规模就够用了。
2. 401:重点看 Key 和 Header
401 authentication_error 大多数时候都和认证有关。
常见原因其实很朴素:
- API Key 根本没配置;
- 线上环境变量没有注入;
- 程序读到了错误环境的 Key;
AuthorizationHeader 被代理或网关剥掉了;- 本地能跑,但线上容器启动参数少了一项。
排查时可以重点看这些信息:
- 当前运行环境是 dev、staging 还是 prod;
- Key 是否为空,但千万不要打印完整 Key;
- Authorization Header 是否成功构造;
- 代理或网关有没有转发 Authorization Header;
- 最近是否换过密钥,或者改过部署配置。
日志可以写成这样:
"auth_check": {
"api_key_present": true,
"api_key_prefix": "sk-ant-***",
"authorization_header_present": true
}
注意,这里只能记录布尔值或极少量前缀,不能把完整 API Key 写出来。这个底线一定要守住。
3. 403:多半是权限或访问范围问题
403 通常不是“请求格式写错了”,而是当前身份没有权限访问某些资源。
可以从这几个方向查:
- 当前账号是否有目标模型权限;
- 组织、项目或区域是否有限制;
- 这个 Key 是否属于预期账号;
- 调用的能力是不是当前接入方式支持的。
如果你用的是第三方 Claude API 兼容接入服务,例如 ClaudeAPI,那也要注意:它不是 Anthropic 官方服务。模型支持情况、线路、充值、开票、中文支持、基础技术协助等能力,都应以平台最新说明为准,不能直接照搬官方账号权限来判断。
4. 404:看路径、模型名和版本
404 not_found_error 常见原因包括:
- endpoint 写错;
- API 路径版本不匹配;
- 模型名拼错;
- SDK 默认路径和实际接入平台不一致;
- 代理转发规则配置错误。
排查时建议记录 endpoint、base_url、model 和 SDK 版本。不要只看代码里的配置,最好确认运行时最终发出的 URL。很多问题不是代码看起来错了,而是线上配置和你以为的不一样。
5. 408 / timeout:看超时配置,也要看整条链路
timeout 不一定说明 Claude API 本身失败。超时可能发生在很多地方,比如:
- 客户端连接超时;
- 客户端读取超时;
- 反向代理 idle timeout;
- 企业网络代理超时;
- stream 首包等待太久;
- 前端连接提前断开。
这类场景建议同时记录:
{
"latency_ms": 60012,
"timeout_ms": 60000,
"stream": true,
"first_token_latency_ms": 8500,
"last_event": "content_block_delta"
}
如果 latency_ms 非常接近 timeout_ms,那就先看客户端超时阈值是不是设置得太短。如果是 stream 中途断开,则要继续看最后收到的事件,以及代理的 idle timeout 配置。
6. 429:不能只理解成“过会儿再试”
429 rate_limit_error 表示限流,但限流背后的原因不一定相同。可能是请求数太高,也可能是并发太高,或者单次输入太长导致 token 消耗太大。
排查时重点看:
- 每分钟请求数是不是突然升高;
- 有没有批量任务同时启动;
retry_count是否快速上涨;- 输入上下文是不是过长;
- 是否多个服务实例共用同一个 Key;
- 有没有队列削峰和并发控制。
处理上可以考虑:
- 使用指数退避;
- 控制并发;
- 批量任务进入队列;
- 降低单次请求的 token 消耗;
- 给重试设置上限;
- 每次重试都记录
attempt_id。
最忌讳的是对 429 无限重试。这样不但解决不了问题,还容易形成重试风暴,把原本可控的限流变成更大的故障。
7. 500 / 502 / 503 / 529:看 request-id、时间窗口和降级方案
5xx 或 529 更像是服务端、上游或临时过载类问题。不过也不能看到 5xx 就直接下结论,还是要看它是不是集中发生在某个时间段、某个模型、某条线路或某个实例上。
一般建议做三件事:
第一,记录每次失败的 request_id。
第二,对可重试错误做有限重试,而不是无限重试。
第三,准备业务降级方案,比如备用模型、非流式兜底,或者给用户一个“稍后再试”的友好提示。
这类日志可以这样记录:
{
"status_code": 529,
"error_type": "overloaded_error",
"request_id": "req_xxx_redacted",
"attempt_id": 2,
"retry_count": 1,
"backoff_ms": 1200
}
5xx 和 529 可以重试,但要有节制。能恢复就恢复,不能恢复就降级,别把重试本身变成新的压力源。
四、stream 流式响应问题,最好单独看
Claude API 在线上很多问题都出现在 stream 场景里。比如前端只看到半截回答、连接突然断了、首包很慢、SSE 解析失败。麻烦的是,这些问题光看 HTTP 状态码未必看得出来。
stream 请求建议额外记录这些字段:
| 字段 | 作用 |
|---|---|
first_token_latency_ms |
判断是首包慢,还是整体慢 |
last_event |
判断中断发生在哪个事件之后 |
stream_event_count |
判断是否收到过有效内容 |
received_chars |
判断用户是否已经看到部分回答 |
disconnect_reason |
区分客户端断开、代理超时还是服务端错误 |
frontend_timeout_ms |
判断是不是前端先断开 |
gateway_idle_timeout_ms |
判断网关 idle timeout 是否太短 |
一些常见判断方式如下:
- 如果一个 event 都没收到,可能是请求没进入处理、认证失败,或者首包超时;
- 如果收到一部分
content_block_delta后断开,优先查代理 idle timeout、客户端读取超时和网络中断; - 如果服务端返回了错误 event,要记录错误类型和 request-id;
- 如果后端已经收到完整流,但前端只显示半截,那就重点查前端 SSE 解析和渲染逻辑。
stream 失败后要不要自动重试,得看业务场景。聊天类产品里,自动重试可能让用户看到两段不一致的回答,体验反而更差;如果是后台批处理,则可以用幂等任务 ID 控制重试,问题会好处理很多。
五、返回 200 也不代表业务一定没问题
Claude API 返回 200,只能说明接口层面成功了,不等于业务结果一定正确。线上经常会遇到这些情况:回答被截断、工具没有触发、返回内容为空、延迟很高、成本异常。
回答被截断
先看 stop_reason 是否是 max_tokens,再看 max_tokens 设置和 output_tokens。如果每次输出 token 都接近上限,那就不是模型“突然不说了”,而是输出预算不够。
工具调用没触发
不要只看业务层“工具没执行”。要看请求摘要里是否真的传了 tools,tool_choice 是否符合预期,模型返回内容里有没有工具调用结构。
返回空内容
重点看 content 数组结构和客户端解析逻辑。有时候响应其实不为空,只是封装层只取了第一个 text block,把其他 content block 忽略掉了。
延迟过高
可以从输入 token、工具调用次数、网络耗时、首包时间和总耗时几个角度看。长上下文、大工具返回、频繁重试,都会明显拉高延迟。
成本异常
看 input_tokens、output_tokens 和 retry_count。很多成本异常不是某一次请求特别贵,而是失败后反复请求,或者批量任务并发失控导致成本被放大。
六、Claude API 故障排查实战案例
案例一:400,由 tools schema 写错导致
现象:上线新工具后,所有带工具的请求都返回 400。
关键日志:
{
"status_code": 400,
"error_type": "invalid_request_error",
"request_summary": {
"tools_count": 1,
"tool_names": ["query_order"]
}
}
判断过程:普通对话正常,只有带 tools 的请求失败,这说明问题大概率集中在工具定义上。继续检查脱敏后的 schema 摘要,发现某个字段的类型写成了非法值。
修复方案:修正 tool input schema,并在 CI 里增加 schema 校验。
复盘:后续日志里补充 tools_count、tool_names 和 schema 版本号,方便快速定位类似问题。
案例二:401,本地正常但线上失败
现象:本地调用没问题,线上容器返回 401。
关键日志:
{
"status_code": 401,
"error_type": "authentication_error",
"auth_check": {
"api_key_present": false,
"authorization_header_present": false
},
"env": "prod"
}
判断过程:这不是模型问题,也不是网络问题,而是线上环境变量没有注入,导致 Key 和 Authorization Header 都没构造出来。
修复方案:补齐部署环境变量,并在服务启动时增加 Key 存在性检查。
复盘:不要打印完整 Key,但要记录 api_key_present 这种布尔字段。简单、清楚,也足够排查。
案例三:429,批量任务并发太高
现象:夜间批量任务启动后,大量请求返回 429,连在线业务接口也受到影响。
关键日志:
{
"status_code": 429,
"error_type": "rate_limit_error",
"retry_count": 3,
"input_tokens": 12000,
"job_type": "batch_summary"
}
判断过程:批量任务本身并发高,单次输入又大,再叠加多次重试,很容易把限流问题放大。
修复方案:让批处理进入队列,限制并发;对 429 使用指数退避;同时隔离在线请求和离线任务,避免互相影响。
复盘:监控 429 比例、重试次数和 token 消耗。只看错误数不够,还要看它是怎么被放大的。
案例四:stream 中途断开
现象:前端经常只显示半截回答。
关键日志:
{
"stream": true,
"status_code": 200,
"first_token_latency_ms": 3200,
"stream_event_count": 48,
"last_event": "content_block_delta",
"disconnect_reason": "gateway_idle_timeout"
}
判断过程:服务端已经输出了部分内容,所以问题不是“模型没有回答”。真正的问题是流式连接被网关中断了。
修复方案:调整反向代理 idle timeout,让前端、网关和后端的超时配置保持一致;必要时增加心跳,或者优化流式处理逻辑。
复盘:stream 日志一定要记录最后一个事件和断开原因。否则只能猜,很难定位。
案例五:529,服务端短时间过载
现象:某个时间段偶发 529,重试一次后大多成功。
关键日志:
{
"status_code": 529,
"error_type": "overloaded_error",
"request_id": "req_xxx_redacted",
"retry_count": 1,
"backoff_ms": 1000
}
判断过程:错误集中在短时间窗口内,并且有限重试后恢复,比较符合短时过载的特征。
修复方案:对 529 使用指数退避和有限重试,同时提供降级提示。
复盘:收集多个 request-id,判断这是单次偶发异常,还是某个时间段内的系统性问题。
案例六:200 但内容被截断
现象:接口返回成功,但回答总是在关键位置停住。
关键日志:
{
"status_code": 200,
"stop_reason": "max_tokens",
"max_tokens": 512,
"output_tokens": 512
}
判断过程:输出 token 已经达到上限,所以不是模型拒答,也不是网络失败,而是 max_tokens 设置太小。
修复方案:提高 max_tokens,或者拆分任务,控制输入长度。
复盘:成功响应也要记录 stop_reason 和 usage。很多问题不是失败请求才有线索,成功请求里同样有。
七、日志脱敏,以及提交排查信息的模板
Claude API 请求日志里可能包含很多敏感信息,比如 API Key、用户输入、系统提示词、文件内容、订单号、手机号、邮箱、内部 URL、工具调用参数等。排查时一定要脱敏。
不要记录或公开这些内容:
- 完整 API Key;
- Authorization Header;
- 用户原始敏感输入;
- 完整系统提示词;
- 完整文件内容;
- 未脱敏的工具调用参数;
- 可以反推出用户身份的 metadata。
可以提供的信息包括:
- 故障发生时间;
request_id;- HTTP 状态码;
error.type;- 模型名;
- 是否 stream;
- SDK、语言和版本;
- 脱敏后的请求摘要;
- 重试次数;
- 影响范围;
- 最近是否改过 prompt、tools、模型、代理或限流配置。
提交问题时,可以按这个模板整理:
发生时间:
业务 trace_id:
Claude API request_id:
HTTP status_code:
error.type:
模型:
是否 stream:
SDK/语言/版本:
脱敏请求摘要:
重试次数:
影响范围:
最近变更:
已尝试的修复动作:
单独发错误截图通常不太够。截图里往往缺少 request-id、时间窗口、请求摘要和重试信息,排查起来还是得来回补材料。
八、生产环境里,提前把排障能力做好
为了让后续 Claude API 故障更容易定位,建议在工程层面提前做一些准备:
第一,统一封装 Claude API client,不要每条业务线各写一套调用和日志逻辑。
第二,自动记录 request_id,并且和业务 trace_id 绑定。
第三,对 400、401、403 这类确定性错误,不要做无意义重试。
第四,对 429 做指数退避、队列削峰和并发控制。
第五,对 5xx、529 做有限重试,同时准备降级方案。
第六,单独监控 stream 中断率、首包时间、P95/P99 延迟。
第七,记录模型、SDK、代理、prompt、tools 等配置变更。很多线上问题,其实都和“刚改过什么”有关。
第八,对高风险调用准备备用模型、非流式兜底,或者友好的错误提示。
另外,日志默认要脱敏,生产环境里不要把完整请求体直接写进去。这一点看似基础,但非常重要。
结尾:Claude API 排障优先级清单
遇到 Claude API 报错时,可以按下面这个顺序排查:
- 先看
request_id和status_code; - 再看
error.type和脱敏后的请求摘要; - 400 重点查请求结构、模型名、messages、tools、max_tokens;
- 401 重点查 Key、Header、环境变量和代理转发;
- 429 重点查并发、token 消耗、批量任务和重试次数;
- 5xx/529 重点看时间窗口、request-id、有限重试和降级;
- stream 问题单独看首包时间、最后 event、断流位置和 timeout;
- 200 但结果异常,要看
stop_reason、usage、工具调用和客户端解析; - 不要上传未脱敏日志,也不要只提供错误截图。
说到底,可靠的 Claude API 排障,不是靠背熟所有错误码,而是让每一次失败请求都有迹可循:能追踪、能关联、能脱敏、能复盘。这样排查问题时,就能从“猜原因”变成“按证据定位问题”。
更多推荐

所有评论(0)