Provider API: region-restricted Claude 5.5 models return 403 "Authentication failed", which makes Claude Code prompt `/login` (and `thinking.type.enabled` 400 is double-encoded)

Đang mở
#1,037 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
38/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Lĩnh vực
api, backend

Hướng nghiên cứu

The report points to the error mapping for POST /provider/v1/messages but names no source files or tests. Start by locating that endpoint and tracing how upstream region refusals and invalid_request_error responses are converted. Done means region refusals have a distinct code while plan and credential errors retain their meanings, and the thinking.type=enabled error has a flat message.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Provider API: region-restricted Claude 5.5 models return 403 "Authentication failed", which makes Claude Code prompt /login; the same endpoint also 400s on thinking.type=enabled with a double-encoded message

No API keys, account ids, prompts, or org names are included below.

Summary

Two separate defects, both hit through POST https://api.commandcode.ai/provider/v1/messages with a GOAT plan:

  1. Region-restricted models surface as a credential error. For claude-sonnet-5-5 / claude-haiku-5-5, when the request is refused for region/edge reasons the API answers with the credential-shaped body 403 permission_error "Authentication failed. Please check your credentials.". Because clients key on permission_error / authentication_failed, Claude Code turns this into Please run /login · API Error: 403 ... — a login prompt for a key that is fine and never stopped working for other models.

  2. thinking.type.enabled is rejected with a nested, double-encoded error. Clients that still send the pre-5.5 thinking shape get 400 whose message is a stringified JSON document rather than a message, so it can't be pattern-matched or shown usefully.

This is the same class as #930 (region restriction mapped onto an auth error) and the same models as the Korea report in #945 (HKG edge 403 ×16 / NRT edge 200 ×5, same key, seconds apart). Filing separately because the client-visible harm here is the /login prompt, and because of the second defect.

Actual Behavior

1) Region-restricted refusal, credential-shaped
HTTP/2 403
{"type":"error","error":{"type":"permission_error","message":"Authentication failed. Please check your credentials."}}

What the client does with it (Claude Code 2.1.300, same key, same session):

Please run /login · API Error: 403 Command Code: Authentication failed. Please check your credentials.

...with the persisted record marked "error": "authentication_failed", "apiErrorStatus": 403. The user is told to re-authenticate a credential that is valid; re-login does not help, and the next model choice works.

2) thinking.type.enabled
POST /provider/v1/messages
{"model":"claude-sonnet-5-5","max_tokens":4096,
 "thinking":{"type":"enabled","budget_tokens":2048},
 "messages":[{"role":"user","content":"hi"}]}

HTTP/2 400
{"type":"error","error":{"type":"invalid_request_error","message":
 "{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":
 \"\\\"thinking.type.enabled\\\" is not supported for this model. Use \\\"thinking.type.adaptive\\\" and \\\"output_config.effort\\\" to control thinking behavior.\"},
 \"request_id\":\"req_...\"}"}}

The text is correct but it arrives as an escaped JSON string inside message, so it is not message-shaped and not matchable. thinking.type=adaptive + output_config.effort returns 200.

Expected Behavior

  • A region/edge refusal should not reuse authentication_failed. A distinct code is enough, e.g.
    403 {"error":{"message":"claude-sonnet-5-5 is not available in your region.","type":"permission_error","code":"MODEL_NOT_AVAILABLE_IN_REGION"}}
    — MODEL_NOT_IN_PLAN for plan gates is already correct and should not regress; 401 authentication_error should stay reserved for real credential problems.
  • The thinking.type.enabled rejection should return the inner message as a flat message string (not stringified JSON).

Evidence

Region-shaped refusal — our account, 2026-10-16, all times CST (UTC+8)

Same key throughout; no config change; deepseek/deepseek-v4.1-flash on the same key and same endpoint was 31/31 × 200 in the same window, which is the control proving the key and the endpoint were healthy.

Window Attempts on 5.5 models 200 403 400
11:00:30 → 12:04:58 34 0 32 2
  • Last success before the window: 10:04:47
  • First success after the window: 19:11:12 (≈ 9h later, nothing changed on our side)
  • Clients hit: claude (Claude Code), curl, Python-urllib, pi — 4 different shapes, all 403, so it is not a header or body problem.
  • 403 latencies were 21–675 ms, i.e. rejected upstream, not our request being wrong.
  • 3 of them came back as 403 Command Code: CAPTURED in 21–29 ms (edge/WAF shape).

This also corrects one point in the support reply for our account: there were no claude-sonnet-5-5 successes interleaved with the refusals between 11:04 and 11:07 on this key — the run was a clean 34-attempt, zero-success window.

Region is the variable, not the account (reproducible shape)

Same key, same second, only the egress changes:

Egress Cloudflare loc Result
JP tunnel node JP / colo=KIX 200 ×N
CN residential (Shanghai) CN / colo=LAX 200 ×N

And in #945 a Korea user showed the same split on these exact two models: every cf-ray ending -HKG failed, every -NRT succeeded. So the refusal follows the edge the request lands on, which is consistent with the support reply — the problem is only that the wire format says "your credentials are bad".

Steps to reproduce

  1. On a GOAT plan, POST /provider/v1/messages with model=claude-sonnet-5-5, max_tokens: 16, one user message — from an egress whose Cloudflare colo is a refused one (e.g. HKG):
    → 403 {"type":"error","error":{"type":"permission_error","message":"Authentication failed. Please check your credentials."}}
  2. Same key, model=claude-sonnet-5 → 403 MODEL_NOT_IN_PLAN (auth passed; plan gate reported correctly).
  3. Same key, model=deepseek/deepseek-v4.1-flash via /provider/v1/chat/completions → 200.
  4. Point Claude Code at the Provider API and send one message while the egress hits the refused colo → the client shows Please run /login, persists authentication_failed.
  5. Send thinking: {"type":"enabled","budget_tokens":2048} to claude-sonnet-5-5 → the double-encoded 400 above.

Command Code Version

Not applicable — reproduced against the Provider API directly (curl), no cmd installed on the machine. Claude Code 2.1.300 was the affected client for defect 1.

Operating System

macOS

Terminal/IDE

zsh + curl 8.x; Claude Code 2.1.300

Shell

zsh

Session file (optional)

Not attached — it contains the whole session. The two lines that matter are quoted verbatim above.

Fix prompt (optional)

Two things to fix, both in the Provider API error path.

  1. Region refusals must not be authentication_failed.
    Location: whatever maps the upstream Anthropic refusal into /provider/v1/messages responses — today it collapses to 403 permission_error "Authentication failed. Please check your credentials.".
    Correct: return a distinct code (e.g. MODEL_NOT_AVAILABLE_IN_REGION) and a message naming the model. Keep MODEL_NOT_IN_PLAN for plan gates and 401 authentication_error for real credential failures. Evidence that the reason is known internally: the CLI already prints the region reason for other models (see #930), and the API itself reports plan gates correctly on the same key.

  2. Stop double-encoding upstream invalid_request_errors.
    Location: the same mapping layer. When the upstream body is {"type":"error","error":{"type":"invalid_request_error","message":"..."}}, the inner message is being stringified into the outer message. Flatten it so message is the plain sentence.

How to check: from an egress that lands on a refused colo, curl the three model cases in the Evidence table — expect MODEL_NOT_AVAILABLE_IN_REGION, MODEL_NOT_IN_PLAN, 200 respectively; and thinking.type.enabled should return a flat invalid_request_error message.

Ngôn ngữ chính
Không có dữ liệu ngôn ngữ
Star
4.2k
Fork
377
Merge trung bình
1 ngày 17 phút
Pull request đã merge (30 ngày)
7

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của CommandCodeAI/command-code

Tất cả issue của CommandCodeAI/command-code

Issue tương tự

Thêm issue về Backend & API Design

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.