| 症状 | 原因 / 修复 |
|---|---|
ChatGPT auth not found; run codex login |
shunt 无法读取 ~/.codex/auth.json。运行 codex login。 |
映射模型上的 authentication_error |
提供方凭据过期/缺失 —— 重新运行 codex login,或 export OPENAI_API_KEY。shunt 会透出后端真实的 detail 消息。 |
| 当 Code Assist 关闭流时 Gemini 失败而不是完成 | Gemini 成功响应需要明确的 provider finish 和正常的 transport closure。没有该 finish 的 EOF 或 [DONE]、无效 UTF-8、格式错误 JSON 或不受支持字段、超大数据、多个 candidate、终端后的数据或截断的响应都会明确失败。流式和一元模式应用相同的终端和提供方错误语义,绝不将嵌入式提供方错误转换为合成成功。 |
| Gemini 工具历史在调度前被拒绝 | 客户端可见的 tool_use ID 在 Gemini 3 上保留真实的 Code Assist 调用配对和 thoughtSignature。请在 tool_result 中返回匹配的 ID。格式错误、重复或孤立的元数据不予修复直接拒绝。从其他提供方导入的工具调用(例如故障转移之后)不带 Gemini 签名;在 Gemini 3 上,该轮的第一个此类调用会附带 Google 文档中给出的占位签名发送,而不会被拒绝。有效结果成为下一个请求中的 functionResponse。shunt 不会持久化或回写此元数据。 |
| Gemini 的瞬态状态不会重试到同一上游 | Gemini 生成是非幂等的。只有在响应标头之前证实发生的瞬态连接或超时故障,才会使用相同选定的令牌、项目和载荷进行重试。返回的状态和所有正文阶段故障都会立即显现,而不向同一上游重试。例外情况是使用单一凭据的 auth = "antigravity_oauth" 提供方:它仍会按照该提供方的 retry 策略重试返回的 429/502/503/504/529 状态;池化的 Antigravity 账户从不重试。 |
400 … model is not supported when using Codex with a ChatGPT account |
你用了一个 -codex slug(或一个你账户未被授权的 slug)。使用 models.json 中一个已授权的 slug(例如 gpt-5.6-sol、gpt-5.5),或设置 upstream_model。 |
网关日志 upstream refused the account; skipping it until the window ends(或写着 You have reached your specified API usage limits、Your credit balance is too low 的 400/402) |
Anthropic 协议的上游(原样转发这些拒绝的 kimi_oauth 池除外)拒绝了整个账户:API 用量上限、信用余额耗尽,或 402 billing_error。shunt 会前进到链中的下一个上游,refusal 字段说明属于哪种情况。只有没有其他上游处理该请求时,客户端才会看到 400/402。非 passthrough 的上游随后会被新请求跳过,直到 You will regain access on … UTC 所示时间,没有则跳过 1 小时,只有重启才会清除跳过状态,重新加载配置不会。要消除原因,用量上限需提高上限,信用余额需充值,billing_error 需在 Anthropic Console 中修正账单和付款信息;但这不会提前结束当前时段:跳过会持续到时段结束,链中所有条目都被跳过时,按配置的顺序尝试该链。据报告,套餐内包含的 Max API 额度会拒绝 Claude Code 流量,因此由它支持的共享上游,在这 1 小时内也可能被其他客户端跳过。在 Claude OAuth 池中,被拒绝的账户会在该时段内冷却,请求轮换到下一个账户;只有当池的最后一次响应是拒绝时,池上游本身才会被跳过,直到其中最早的账户可再次处理请求。 |
/model 没有列出你的模型 |
对于 gpt-* id 使用 ANTHROPIC_CUSTOM_MODEL_OPTION;发现 只暴露 claude/anthropic 前缀的 id。 |
opus 选择 Opus 4.7 / sonnet 选择 Sonnet 4.6 |
Claude Code 的内置别名表会为网关会话钉住这些层级。使用 ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-5 在客户端钉住层级,或在 shunt 中重映射 id —— 见模型别名。 |
| Opus/Fable 的上下文窗口显示 200K | 只有 base URL 为 api.anthropic.com 时,Claude Code 才会信任模型的原生 1M 窗口。选择 opus[1m] / fable[1m] —— 见模型别名。 |
/model 中缺少 Fable,或 claude-fable-5 回退到 Opus |
使用普通 ANTHROPIC_BASE_URL 时,Fable 会被过滤掉。设置 ANTHROPIC_DEFAULT_FABLE_MODEL=claude-fable-5,或使用网关登录 —— 见模型别名。 |
| 发现从不触发 | 它被门控在一个网关凭据(ANTHROPIC_AUTH_TOKEN、API 密钥或 apiKeyHelper)加上 CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 上。用 claude --debug → [gatewayDiscovery] 行调试。 |
config check failed |
运行 shunt check 查看确切原因(bind 地址、路由中的未知提供方、错误的适配器/认证)。 |
| Claude Code 要求你登录 | 设置一个 shunt 能为未映射模型转发的 Anthropic 凭据(ANTHROPIC_AUTH_TOKEN / 登录)。仅有一个 base URL 不是凭据。 |
映射模型上力度卡在 medium |
设置 CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1 —— 见 力度与上下文。 |
映射模型上出现 400 Invalid schema for function '…': '…' is not a 'regex' |
OpenAI 后端用 Python 的 re 编译工具 schema 中的每个 pattern,因此会拒绝仅 JavaScript 支持的正则(\p{Cc}、(?<name>…)、\u{…});Claude Code 的 Artifact 工具就带有这样的 pattern。shunt 会从转发的 schema 中去掉这些 pattern。strict 模式外 pattern 只是建议,失去的只是一个提示,但 description 并不总会重述该约束。若仍看到此错误请升级。 |
| 映射模型上的工具调用带着所有可选参数填充到达 | 后端会把没有显式 strict 的 function 工具向 strict 模式归一化,该模式下闭合的参数对象(additionalProperties:false)表现得像所有属性都是必填,于是模型会填充调用方从未设置的值。shunt 在转发的 function 工具和 tool_search_output 中回放的规格上固定 strict:false,让可选属性保持可选; xAI/Grok 路由上未验证故不发送该字段,原生 tool_search 的 schema 没有可选属性因此省略。若仍看到此现象请升级。 |
| 映射模型上工具搜索未生效(每轮都发送全部工具 schema) | 设置 ENABLE_TOOL_SEARCH=true。Claude Code 在非第一方 base URL 背后会自动禁用乐观式工具搜索;shunt 会转发 tool_reference 块并按需揭示延迟的 schema —— 见 ChatGPT / Codex → 工具搜索。 |
400 Deferred custom tools are only supported on Anthropic models… Received stealth/ox-alpha(或其他非 Claude slug) |
Claude Code 在非 Anthropic 的 Messages 模型上发送了 defer_loading / tool_search_tool_*。shunt 会在转发前从非 claude* / 非 anthropic/* 的上游 id 中去掉这些字段;OpenRouter 上的 Anthropic slug 仍保留该协议。 |
| 工具搜索能工作,但不收回上下文(仍在为文本 shim 的缓存失效代价买单) | 原生 tool_search 在默认(“auto”)模式下,只对已确认支持该协议的宿主生效 —— ChatGPT/Codex 后端和 api.openai.com —— 并且仍需通过风格/模型门控(标准 OpenAI/ChatGPT-Codex 风格、gpt-5.4 及以上模型)。自定义的 OpenAI 兼容端点(LiteLLM、vLLM、OpenRouter、自托管)不会自动启用;请在确认它实现了 tool_search 条目后设置 tool_search = true。如果你期望在已知宿主上使用原生协议但仍在走 shim,请确认你没有设置 tool_search = false —— 见 ChatGPT / Codex → 工具搜索 → 原生协议。 |
| 映射模型上下文长度错误后会话卡住 | shunt 会把上游溢出错误重写为 prompt is too long …,使 Claude Code 自动压缩并重试 —— 见 上下文溢出恢复。如果每隔几轮就复现,把 CLAUDE_CODE_MAX_CONTEXT_TOKENS 降到模型的真实窗口。 |
| Cloudflare 后流断掉(524) | 把 sse_keepalive_seconds 保持在默认值(30)而非 0。 |
| 共享网关上映射模型返回 401 | 客户端 token 缺失/无效 —— 设置 ANTHROPIC_AUTH_TOKEN=<token>(以 Authorization: Bearer 被接受,仅池化网关)或 ANTHROPIC_CUSTOM_HEADERS="x-shunt-token: <token>"(混有透传模型时推荐,让每个调用方真实的凭据保有自己的槽位;确实出现在 Authorization / x-api-key 中的客户端 token 会从该槽位清除而不是被转发);见 共享网关。 |
| Anthropic 适配器模型返回 429 | 检查网关日志中的 rate_limit_kind。quota(带有 retry-after / anthropic-ratelimit-* 头部)是真实的速率限制 —— 请退避或减少并行负载。client-shape-rejection(OAuth 请求、两种头部都没有、body 只有 "Error")表示 api.anthropic.com 拒绝了一个不像 Claude Code 的订阅 OAuth 请求 —— 非 Claude Code 客户端必须使用 API 密钥而不是 OAuth token。部分 Claude Code 请求到达上游时缺少它所检查的 identity 块 —— auto 模式权限分类器,以及 CLAUDE_CODE_ATTRIBUTION_HEADER=0 时 mod 的 $.model.complete 之类的旁路查询。shunt 会在 auto 模式分类器以及任何不带可接受标记的 Claude Code 请求(通过 x-claude-code-session-id、claude-cli/ user agent 或 x-app: cli 识别)上恢复 identity。该恢复仅在发出的 bearer 是订阅 OAuth 令牌时生效,不作用于上游并不检查的 count_tokens,空的 x-claude-code-session-id 也不算 Claude Code。它用于避免 auto 模式以“model temporarily unavailable”fail-closed,以及旁路查询被这种不带头部的 429 拒绝;quota 429 仍会原样返回给客户端。其他客户端以及其余请求除 Anthropic 提供方页面所述的 thinking signature 移除之外,均保持 byte-for-byte 透传。若要让这些检查不落在昂贵的模型上,可用 classifier_model 固定它们。no-ratelimit-headers(非 OAuth 凭据)是缺少速率限制元数据的提供方 429 —— 按 quota 处理。 |
| Claude Code 提示本会话不符合 auto 模式免费分类器请求的条件 | auto 模式会请求 API 在服务端对每次工具使用进行分类,而只有 api.anthropic.com 会作出响应。在 Anthropic 路由上,shunt 原样中继该请求及其响应。由其他上游处理的路由(Codex/Responses、Cursor、Gemini、Antigravity)没有可中继的服务端判定,因此 shunt 不是什么都不返回,而是逐个动作回复“无法评估”:这样 Claude Code 只在本地分类那一个动作,并在下一轮继续询问服务端,而不会在本会话剩余时间内放弃服务端分类器、改用自己的计费分类器。对 Anthropic 协议的第三方(Kimi、OpenRouter、DeepSeek 等),在请求发出前还会剥离 safeguards 字段及其 dangerous-tool-use-* beta token:没有任何一家被确认接受该字段,而一旦返回点名该字段的 400,Claude Code 会在 /clear 之前拒绝 auto 模式的所有工具使用;剥离字段则只损失该轮的服务端判定。若要完全关闭该功能并让分类留在客户端,请设置 CLAUDE_CODE_AUTO_MODE_SERVER=0。 |
共享网关上返回 503 overloaded_error |
网关已达入站并发上限,直接拒绝了该请求而不是排队(body 消息为 too many requests are already in flight,并带 Retry-After: 1)。这是 shunt 自己的准入控制,不是上游 503 —— 上游 503 会带着提供方自己的消息原样中继。请在指定延迟后重试、减少并行负载,或调高 max_concurrent_requests 并重启(该上限在启动时固定)。 |
shunt run 启动时以 store … 错误退出 |
可选的 [store] 打开失败。连接和打开类错误会给出存储目标(绝不含密码);下面的快照导入错误则给出子系统、快照路径和原因。is held by another shunt instance (<host>:<pid>):另一个 shunt 进程正在使用该存储;请停止它,若是崩溃之后,则最多等待 hold_stale_seconds(默认 30 秒)直到其 hold 变为 stale。启动时出现 store statement failed: … lock timeout(PostgreSQL):另一个会话把实例 hold 行锁住了 5 秒,通常是客户端已断开的后端;在 pg_stat_activity 中找到它并用 pg_terminate_backend 结束,或等待服务器回收它,然后重启。is at schema version N, newer than the M this binary supports:数据库已被更新的 shunt 迁移过,且未被改动;请升级 shunt。importing the <subsystem> state snapshot <path> into the store failed:该子系统没有导入任何内容。但如果原因是提交失败,导入可能已经生效,下次启动时会将其视为已导入。请查看冒号之后的原因:JSON 快照格式错误或版本不受支持,就修复或删除错误中指出的文件;文件无法读取,就修复其权限或路径;存储事务失败,就检查存储本身。然后重启。is unreachable:无法连接或创建数据库;shunt check 无需启动即可输出同样的原因。requires a build with the store-postgres feature:请使用发布二进制,或用 --features store-postgres 构建。 |
网关 /oauth/token 返回 503 temporarily_unavailable |
配置了 [store] 时,token 无法写入,因此没有签发,变更已回滚。如果是 refresh token 轮换,存储恢复可写后,客户端重试即可成功。如果是 device flow 登录,已批准的 grant 已被使用,重试的 poll 会收到 expired_token,需要重新开始登录。无论哪种情况,都请检查 shunt.store.write_failures{subsystem="gateway"} 计数器以及存储的磁盘或连接。如果警告日志中的原因是 postgres … did not complete within 15s,说明某次 PostgreSQL 往返在 15 秒内没有得到应答,原因可能是连接不再应答,也可能是服务器太慢。shunt 对每次数据库往返在 15 秒后放弃,因此排在停滞写入之后的 token 请求只会等待有限的时间,而不是无限期等待。如果放弃的是 COMMIT,shunt 会重新读取存储确认:写入已生效时,请求成功,只留下这条警告。 |
完整的网关故障排查表见 将 Claude Code 连接到 LLM 网关。