Skip to content

bug(client-detector): /v1/messages/count_tokens 端点缺失 metadata.user_id 导致 Claude Code CLI 被误判并拦截 #1363

Description

@sususu98

问题描述

当客户端使用标准的 Claude Code CLI 调用 Anthropic 的 /v1/messages/count_tokens (计算 Token 数) 端点,且匹配到的供应商配置了客户端限制规则(例如允许客户端开启了 claude-codeclaude-code-cli)时,请求会被拒绝并返回 503 Service Unavailable (no_available_providers),供应商原因提示为 client_restriction

根源分析

  1. 客户端精确检测逻辑 (confirmClaudeCodeSignals)
    src/app/v1/_lib/proxy/client-detector.ts 中,confirmClaudeCodeSignals(session) 要求必须同时收集到以下 4 个信号 才会设置 confirmed = true(确认属于 Claude Code 客户端):

    • x-app-cli (x-app: cli Header)
    • ua-prefix (User-Agent: claude-cli/... Header)
    • betas-present (anthropic-beta Header)
    • metadata-user-id (Body 中存在 request.message.metadata.user_id)
  2. count_tokens 端点行为
    Anthropic 规范和 CLI 实际实现中,POST /v1/messages/count_tokens 的请求 Payload 仅包含 model, messages, system, tools, thinking 等字段,不会也不应该包含 metadatametadata.user_id

  3. 误判过程

    • 对于 count_tokens 请求,CCH 只能收集到前 3 个 Header 信号,缺少第 4 个 metadata-user-id 信号。
    • confirmClaudeCodeSignals 判定 signals.length === 3 !== 4,导致 claudeCode.confirmed = false
    • 供应商在检查客户端限制时,matchClientPattern(session, "claude-code") 返回 false,判定为 allowlist_miss,记录 reason: "client_restriction" 并剔除该供应商。
    • 若匹配的模型仅有该 Claude 供应商可用,请求最终报错 503 Service Unavailable (no_available_providers)

客户端请求与响应示例 (已脱敏)

HTTP Request:

POST /v1/messages/count_tokens?beta=true HTTP/1.1
Accept: application/json
Authorization: Bearer sk-***
Content-Type: application/json
User-Agent: claude-cli/2.1.220 (external, cli)
X-Claude-Code-Session-Id: e08f04cb-****-****-****-************
anthropic-beta: claude-code-20250219,interleaved-thinking-2025-05-14,context-management-2025-06-27,token-counting-2024-11-01
anthropic-dangerous-direct-browser-access: true
anthropic-version: 2023-06-01
x-app: cli
Host: cch.example.com

{"model":"claude-opus-5","messages":[{"role":"user","content":"..."}]}

HTTP Response:

HTTP/1.1 503 Service Unavailable
Server: nginx
Content-Type: application/json; charset=utf-8

{
  "error": {
    "message": "No available providers (cch_session_id: sess_****)",
    "type": "no_available_providers",
    "code": "no_available_providers",
    "details": {
      "totalAttempts": 1,
      "excludedCount": 0,
      "filteredProviders": [
        { "id": 124, "reason": "client_restriction" },
        { "id": 119, "reason": "format_type_mismatch" }
      ],
      "clientRestrictedProviders": [
        { "id": 124, "reason": "client_restriction" }
      ]
    }
  }
}

建议解决方案

src/app/v1/_lib/proxy/client-detector.ts 中:

  • 识别请求端点是否为 count_tokens(或非对话端点)。
  • 如果是 count_tokens 请求且前 3 个 Header 信号(x-app-cli + ua-prefix + betas-present)均满足,或允许在缺少 metadata.user_id 时进行合理的适配判定,避免误判。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions