1. 先搞清楚这个 400 到底在报什么
Extra inputs are not permitted这个报错,字面意思是「不允许出现额外输入」,它跟你的 API Key 有没有余额、模型名写没写对基本没关系。它出现的位置很固定:请求已经打到 Anthropic 侧(或 Bedrock 侧),服务端在解析请求体时发现了它不认识的字段,于是直接 400 拒绝。
真正让人困惑的地方在于:同样的 Claude Code、同样的配置,直连的时候一切正常,一旦中间加了一层代理、网关或者中转服务,就开始报这个错。很多人第一反应是去改模型名、换 Key、降版本,折腾一圈发现没用,因为问题根本不在这些地方。
这个报错的核心检索词就是 Claude、Extra inputs are not permitted、代理、Beta 标头。它适合谁看?适合所有用 Claude Code 或自己写脚本调 Anthropic API、并且请求链路里存在一层转发的人。典型触发场景是:你用了 LiteLLM、Nginx 反代、OpenRouter 这类网关,或者后端接的是 AWS Bedrock、Vertex AI,然后 Claude Code 版本又比较新(2.1.22 之后引入了实验性 Beta 功能)。
一句话概括成因:Claude Code 在请求体里塞了 Beta 字段(比如defer_loading、context_management),同时在请求头里用anthropic-beta声明「这些字段是我主动开启的实验特性,请放行」。代理把请求头剥掉了,但请求体原样转发,服务端看到一堆没人声明的陌生字段,就判定为非法输入。下面从请求头透传的角度,把定位和修复一步步拆开。
2. 前置准备:用 TaoToken 打通调用链路
在动手排查之前,先把调用链路固定下来,避免一边查标头一边还在怀疑 Key 和地址。我这边统一用 TaoToken 作为接入层,它的好处是地址和 Key 管理集中,出问题时能快速区分「是链路问题还是标头问题」。
官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个地址不加 UTM 参数,直接填进配置即可)。
你需要先拿到一个可用的 Key。进入控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后在 API Keys 页面生成密钥:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。生成后先复制保存,页面刷新后就不再完整显示。
如果你只是想先确认模型本身能不能通,可以打开模型对话页发一条消息试试:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。这一步的意义是建立一个「基线」——如果对话页正常、Claude Code 报 400,那问题几乎可以锁定在 Claude Code 发出的请求头/请求体上,而不是账号或网络。
接入文档在这里,配置字段和参数说明以它为准:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。把 Key 和 Base URL 准备好之后,再进入下面的配置环节。
3. 可复制配置:让 Beta 标头正确透传
修复思路只有两条:要么让代理正确转发anthropic-beta标头,要么让 Claude Code 干脆别发这些 Beta 字段。先讲透传方案,再讲禁用方案,你可以按自己是否需要 Beta 功能来选。
3.1 先确认 Claude Code 侧发了什么
在改代理之前,先让 Claude Code 把请求头打出来,确认它确实发了anthropic-beta。设置调试环境变量后启动:
export CLAUDE_CODE_DEBUG=1 claude日志里会看到请求头部分,重点找anthropic-beta这一行,正常应该类似:
anthropic-beta: prompt-caching-scope-2026-01-05,defer-loading-2026-03-15如果这里就没有,那问题在 Claude Code 配置;如果有,但代理侧收不到,问题就在代理。这一步是分水岭,别跳过。
3.2 Nginx 反向代理的标头透传配置
Nginx 默认不会转发所有自定义标头,anthropic-beta这种非标准头很容易被丢掉。需要在 location 块里显式声明转发:
server { listen 443 ssl; server_name your-proxy.example.com; location /v1/ { proxy_pass https://api.anthropic.com/v1/; # 关键:允许并转发 anthropic-beta 标头 proxy_pass_header anthropic-beta; proxy_set_header anthropic-beta $http_anthropic_beta; proxy_set_header Host api.anthropic.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 流式响应必须关掉缓冲,否则 SSE 会被截断 proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; } }proxy_pass_header和proxy_set_header这两行是核心。前者告诉 Nginx 这个头允许通过,后者把客户端发来的值原样传给上游。改完执行nginx -t校验语法,再nginx -s reload生效。
3.3 LiteLLM 网关的两种处理方式
如果你用的是 LiteLLM,它默认对 Anthropic 原生 API 是会转发anthropic-beta的,但接 Bedrock 时 Bedrock 本身不支持这些字段,所以更推荐直接丢弃不支持的参数:
model_list: - model_name: claude-sonnet litellm_params: model: bedrock/us.anthropic.claude-sonnet-4-20250514 drop_params: true additional_drop_params: ["defer_loading", "context_management"]drop_params: true让 LiteLLM 自动过滤后端不认识的参数,additional_drop_params再精确点名几个已知会惹事的字段。这样即使 Claude Code 以后新增 Beta 字段,也不会因为后端不支持而 400。
3.4 最省事的兜底:禁用实验性 Beta
如果你不需要 Tool Search、上下文自动裁剪这些实验特性,最直接的办法是让 Claude Code 不发 Beta 字段。设置环境变量:
export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1想永久生效就写进 shell 配置:
echo 'export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1' >> ~/.zshrc source ~/.zshrc或者在~/.claude/settings.json里配置:
{ "env": { "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1" } }设置后 Claude Code 会自动剥离 Beta 请求头、移除工具 schema 里的defer_loading、清掉请求体里的context_management,标准字段全部保留。大多数情况下这个错误会立刻消失。
4. 验证请求:确认标头完整到达
改完配置不能只看「不报错了」,要确认标头是真的透传过去了。下面给几个可操作的检查动作。
4.1 用最小请求验证标头
先绕开 Claude Code,用 curl 直接打一个带anthropic-beta的最小请求,确认链路本身能透传:
curl -sS https://taotoken.net/api/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: prompt-caching-scope-2026-01-05" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role": "user", "content": "ping"}] }'如果返回正常内容,说明标头能到达;如果返回 400 且提到Extra inputs are not permitted,说明标头在中间被剥了。这一步能把「链路问题」和「Claude Code 问题」彻底分开。
4.2 在代理侧抓包确认
在代理服务器上抓一下 443 端口的流量,过滤anthropic-beta:
sudo tcpdump -i any -A -s 0 'tcp port 443' | grep -i 'anthropic-beta'如果抓不到这个头,说明客户端到代理这一段就丢了;如果抓到了但上游还报错,说明代理到上游这一段丢了。Nginx 也可以在 location 里加临时日志:
access_log /var/log/nginx/anthropic_debug.log; log_format anthropic_debug '$http_anthropic_beta';4.3 回归检查清单
修复后按这个清单过一遍,确认没有副作用:
| 检查项 | 预期结果 |
|---|---|
| 基本对话 | 正常响应,无 400 |
| MCP 工具调用 | 工具可正常连接和调用 |
| 流式响应 | 输出完整,无中途截断 |
/doctor诊断 | 所有检查项通过 |
/usage查询 | 用量信息正常显示 |
流式响应这一项特别容易被忽略。proxy_buffering off没配的话,标头问题解决了,SSE 又会被缓冲截断,表现是回复到一半卡住。
5. 本篇常见错排查
报错依旧但日志里标头明明在。这种情况多半是后端本身不支持这些字段,比如 Bedrock 和 Vertex AI 的 API 规范里就没有defer_loading。标头传得再对也没用,得走drop_params或禁用 Beta 的路线。
改了 Nginx 配置没生效。先nginx -t看语法,再确认 reload 成功。还有一种情况是配置写在了错误的 server 块里,请求实际走的是另一个 location,标头自然没被处理。
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS设了没用。检查是不是设在了当前 shell 之外,或者被settings.json里的其他 env 覆盖了。用echo $CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS确认当前会话真的读到了。
升级 Claude Code 后又复发。新版本可能引入新的 Beta 字段。升级后先跑/doctor,如果出现新的 400,确认是不是新字段导致,然后更新代理的additional_drop_params列表。
OpenRouter 这类网关怎么都传不过去。部分第三方网关对自定义标头支持有限,这种情况别硬刚,直接用禁用 Beta 的方案,或者换成能透传标头的接入方式。
6. 后续怎么调更顺手
排查完这一轮,建议把「代理层统一参数清洗」当成默认动作。在 LiteLLM 里常驻drop_params: true加additional_drop_params,这样 Claude Code 以后新增什么实验字段都不会再触发同类 400。
如果你长期用 Claude Code 做编码和 Agent 任务,可以考虑走 Coding Plan,把调用配额和模型切换集中管理,省得每次都在环境变量上折腾:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。需要自己写脚本或接第三方工具时,Key 还是从 API Keys 页面拿:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,字段含义对照接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
最后留一个我踩过的坑:标头透传和请求体字段清洗是两件事,别只改一个。代理转发了anthropic-beta,但后端是 Bedrock,照样 400;反过来只清洗字段不禁标头,Anthropic 原生 API 那边可能又因为标头声明了不存在的字段而报错。两边对齐,问题才算真正闭环。