Skip to content

Claude Code 2.1.172+ 自定义模型名时在 messages 中注入 role:"system" 消息,严格上游(Vertex/Bedrock类)拒绝导致重试熔断 503 #1265

Description

@00010110

问题描述

Claude Code 2.1.172 配合自定义模型名(通过 ANTHROPIC_DEFAULT_OPUS_MODEL 等指向 cchub 渠道模型,如 aws-claude-opus)使用时,请求经 cchub 转发到严格校验的上游(Vertex / Bedrock 类 Anthropic 兼容端点)会被拒绝。cchub 重试耗尽全部供应商后触发熔断,客户端最终收到:

{"statusCode": 503, "errorMessage": "所有供应商暂时不可用,请稍后重试"}

首次请求的流式响应为:

event: error
data: {"type": "error", "error": {"type": "api_error", "message": "Upstream returned an error event (request_id: ...)"}}

由于错误被包装成 api_error / 503,且重试导致熔断殃及后续请求,排查非常困难。

根因(已通过本地抓包确认)

Claude Code 2.1.172(2.1.153 之后引入的行为变化)会根据模型名是否为内置官方 Claude 模型 ID 切换 skills 列表的注入格式:

模型名 skills 注入方式
官方 ID(claude-opus-4-7 等) user 消息内 <system-reminder> 文本块(旧行为)
任何非官方名(aws-claude-opus、甚至 claude-test-fake-9 messages 数组中独立的 {"role": "system", "content": "..."} 消息

抓包验证(同一 CC 版本、本地 HTTP 捕获服务器):

--model claude-opus-4-7   → messages roles: ['user']            ✅
--model aws-claude-opus   → messages roles: ['user', 'system']  ❌
--model claude-test-fake-9 → messages roles: ['user', 'system'] ❌

Anthropic 官方 API 接受 messages 中的 role: "system",但 Vertex / Bedrock 等转换层严格校验 messages 角色只能为 user / assistant,直接返回 400 类错误("Bad request - check request format and parameters")。cchub 目前原样透传该消息,导致:

CC 2.1.172(自定义模型名)发出 role:"system" 消息
    → cchub 原样转发
    → 严格上游 400 拒绝
    → cchub 重试其余供应商全部失败
    → 熔断 → 503 "所有供应商暂时不可用"

由于同一模型可能挂多个渠道(宽容的渠道能成功),故障表现为间歇性,进一步增加排查难度。

影响范围

所有满足以下条件的用户:

  • Claude Code ≥ 2.1.172(2.1.153 确认无此行为,具体引入版本未逐版定位)
  • 渠道模型名为自定义命名(如 aws-claude-opusqwen-xxglm-xxMiniMax-xx 等,这是 cchub 用户的常见用法)
  • 上游为严格校验 messages 角色的 Anthropic 兼容端点

复现步骤

# 对任意 Vertex/Bedrock 类严格上游渠道发送:
curl -s "https://<cchub>/v1/messages" \
  -H "x-api-key: $KEY" -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "<自定义模型名>", "max_tokens": 100,
    "messages": [
      {"role": "user", "content": "hi"},
      {"role": "system", "content": "The following skills are available: ..."}
    ]
  }'
# → 400/503;去掉 role:"system" 那条消息后同样请求成功

或直接用 Claude Code 2.1.172 + ANTHROPIC_MODEL=<自定义模型名> 发送任意消息(只要本地启用了任意 skill/plugin 就会注入该消息)。

建议修复

参照现有 src/app/v1/_lib/proxy/billing-header-rectifier.ts 的模式(同样是处理 Claude Code 新版本注入的、上游不兼容的内容),新增一个 proactive rectifier:

  • 在转发前扫描 messages,将 role: "system" 的消息取出
  • 其内容合并到顶层 system 数组末尾(string content 转为 {type: "text", text} block)
  • 语义上与 Anthropic 官方对该写法的处理一致,对宽容上游无副作用,对严格上游消除 400

如方案可接受,我可以提交 PR。

环境

  • claude-code-hub: v0.8.5(self-hosted)
  • Claude Code: 2.1.172(user-agent: claude-cli/2.1.172 (external, cli)
  • 对照版本: Claude Code 2.1.153 无此问题

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions