Claude API errors: 401, 404, 429 and 529, diagnosed
Almost every Claude API error reduces to four causes: the wrong auth header, a doubled version segment in the URL, a missing required parameter, or a model ID that does not exist on the endpoint. Check those before anything else.
curl https://aicomp.ai/v1/models \
-H "Authorization: Bearer sk-your-gateway-key"
A JSON list of model IDs means the key is good. Invalid token means it was copied wrong.
Fast diagnosis table
| Status / error | Usual cause | Fix |
|---|---|---|
| 401 authentication_error | Wrong or truncated key, or wrong header | Use x-api-key and include anthropic-version |
| 404 not_found_error | Doubled /v1, or unknown model | Check the base URL and the exact model ID |
| 400 invalid_request_error | max_tokens missing | Add it — it is required on this API |
| 429 rate_limit_error | Your quota on requests or tokens | Exponential backoff with jitter |
| 529 overloaded_error | Upstream under load | Retry with backoff; not your quota |
| 413 / context length | Prompt longer than the window | Compact history or trim context |
| 500 api_error | Server-side fault | Retry once, then alert if it persists |
401 (the header), not the key
The single most common failure when moving from an OpenAI-shaped integration. This API expects:
curl https://aicomp.ai/v1/messages \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":64,
"messages":[{"role":"user","content":"say OK"}]}'
Sending Authorization: Bearer instead returns 401 even with a perfectly valid key.
404 — the doubled segment
If your base URL ends in /v1 and your client appends /v1/messages, the request
becomes /v1/v1/messages. Remove the suffix from whichever side is duplicating it, never from both.
400 — required parameters
max_tokens is mandatory. Requests that omit it are rejected before anything else is evaluated,
which is why this error can appear even when everything else is correct.
429 and 529 — retries done properly
import time, random
def with_retry(fn, retries=5):
delay = 1.0
for _ in range(retries):
try:
return fn()
except RateLimitError:
time.sleep(delay + random.uniform(0, 0.3))
delay *= 2
raise
Never retry a 400 It will fail identically every time. Retry 429, 529 and 500. See rate limits explained for the full picture.
Model not found
Model IDs differ between providers and sometimes include date suffixes. Copy the exact string from the endpoint's own listing rather than from documentation or memory:
curl https://aicomp.ai/v1/models -H "Authorization: Bearer sk-your-gateway-key"
Still stuck
Reproduce with curl from the same machine your app runs on. If curl works and your app does not, the problem is in your client, headers, base URL, or an environment variable that never reached the process.
Error codes, what they actually mean, and how to confirm each
| Symptom | What is actually happening | How to confirm | Fix |
|---|---|---|---|
| 401 on every call, including a plain curl | The key is wrong, truncated, or from a different account than you think. | Run one curl with the key printed to stdout first (whitespace is invisible in most editors). | Regenerate the key and paste it straight from the dashboard with no surrounding spaces. |
| 403 with a valid-looking key | Authentication passed but authorisation did not, the account may be out of credit or restricted. | Check account state in the dashboard; the response body often names the reason. | Top up or lift the restriction; no client-side change will fix it. |
| 404 with a doubled /v1 in the path | The base URL already ends in /v1 and the client appended another segment. | Read the path in the error message The duplication is visible in it. | Remove the suffix from the base URL and let the client build the path. |
| 404 that is actually an HTML page | Something between you and the endpoint answered instead of the API. | Inspect the content type of the response body, HTML means it is not the API. | Check proxies and DNS on the host making the call. |
| 429 under load, then fine a minute later | A per-minute window was exhausted, not a permanent cap. | Plot the errors against requests per minute. | Throttle on the client and back off with jitter rather than retrying immediately. |
| 500 or overloaded responses, intermittently | Upstream saturation. Retrying instantly adds to it. | Check whether error rate rises after retries It usually does. | Back off exponentially, and treat persistent 5xx as a signal to change model or route. |
| Context length error on a request that used to work | The conversation grew past the window; history, tool output and retrieved docs all count. | Compare the reported token count against the model's window. | Compress or reset the session, and trim what you attach per turn. |
| Timeouts only when streaming | A stream holds the connection open; a timeout tuned for non-streaming calls fires early. | Compare wall-clock duration of a stream against your configured timeout. | Set a read timeout appropriate to streaming, not a single global timeout. |
How to confirm the change actually took effect
"I set it and nothing happened" is the most common failure here, and it is almost never the endpoint. It is usually the client: a desktop app that reads its own settings file rather than your shell environment, a process started before you exported the variable, or an SDK client instance built once at import time and reused after the config changed. Verify before you debug anything else.
Layer 1, is the endpoint reachable, and does your key work?
curl -s https://aicomp.ai/v1/models \
-H "Authorization: Bearer $KEY" | head -c 400
A JSON list of model IDs means both are true. An Invalid token body means the
endpoint answered but rejected the credential The URL is right, the key is not. An HTML page
means something between you and the endpoint (a corporate proxy, a captive portal) answered
instead, and no amount of config change will fix it from inside your app.
Layer 2 — who actually answered?
from openai import OpenAI
client = OpenAI(base_url="https://aicomp.ai/v1", api_key=KEY)
r = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{"role": "user", "content": "Reply with the single word: ok"}],
max_tokens=8,
)
print(r.model) # 回显的 model 是谁 —— 这是端点身份的唯一硬证据
print(r.usage) # prompt_tokens / completion_tokens
The model field echoed back is the only hard evidence of identity. A request that
silently fell through to a default provider still returns HTTP 200 and still returns sensible
text, which is exactly why this layer exists. If the echoed model is not the one you asked for,
you are talking to someone else.
Layer 3 — did it land in the usage log?
Reachability is not billing. Open the provider's usage view and look for the model ID you just called, on today's date. If the call succeeded but does not appear, you either hit a cached response or you are still on the old path. This layer is also the one that catches the case where two environments share a key and you are reading the wrong one.
Three ways this check lies to you
- A 200 that is not from the API. Proxies and error pages return 200 with HTML. Check that the body parses as JSON before you trust the status code.
- A stale process. Long-running servers and editors capture configuration at startup. Restart the process, not just the shell.
- A client built before the change. SDK clients snapshot
base_urlat construction. If yours is a module-level singleton, the new value never reaches it.
FAQ
Why do I get a 401 with a key that works elsewhere?
Almost always the header. The Anthropic API authenticates with x-api-key and expects an anthropic-version header, not Authorization: Bearer. Keys are also easy to truncate when copying.
Why 404 on a path that looks correct?
A doubled version segment. If your base URL already ends in /v1, do not add it again before /messages, the request becomes /v1/v1/messages and returns 404.
What does 'max_tokens is required' mean?
Exactly that: unlike the OpenAI API, where the field is optional, this API rejects requests without it. Add max_tokens to every call.
Is a 429 the same as a 529?
No. A 429 means you hit your own rate limit and should back off; a 529 means the upstream is overloaded. Both warrant retry with backoff, but only the first is about your quota.
How do I tell whether the endpoint or my code is at fault?
Reproduce with curl from the same machine. If curl succeeds and your app fails, the problem is in your client configuration (headers, base URL or environment variables).