Cloudflare AI Gateway: the 429 that is not a rate limit, and the 500 that is a 404
Platform: Cloudflare AI Gateway in front of Workers AI and third-party providers. Change: new gateways require authentication from 2026-11-03; rejected provider credentials became HTTP 401 with error code 2009 on 2026-10-06; gateways created on or after 2026-09-24 log under Workers Logs instead of Legacy Logs. Detection chapter reviewed: 2026-11-08. The paid chapter is still in re-verification on a live account; the status notice in the appendix stays until that finishes.
What changed
AI Gateway is not one frozen behaviour. Six separate changes between April and November 2026 alter what an error means, how many provider calls one client request costs, and whether the failure is stored anywhere you can read. Each item below is dated and can be checked against the sources in the appendix.
- New gateways require authentication (2026-11-03). Creating a gateway by API now defaults
authenticationtotrue, andauthentication: falseis rejected with HTTP400. Once on, it cannot be turned off; the documented message isAuthentication cannot be disabled once enabled.Existing unauthenticated gateways keep working, so two gateways in one account now behave differently. - Rejected provider credentials look different (2026-10-06). A provider rejecting your key or stored BYOK key now returns HTTP
401with error code2009. Google Vertex used to return HTTP500and retry the provider; it now fails with401and does not retry. With Unified Billing, a credential rejection returns HTTP503. - Two log systems now coexist (2026-09-24). Gateways created on or after that date follow Workers Logs limits and pricing. Gateways created before it stay on Legacy Logs, whose free plan stores 100,000 logs per account at a 500 logs-per-second storage rate. Comparing log evidence between an old and a new gateway compares two different systems.
- Universal Endpoint and
/compatare deprecated but still serving (docs updated 2026-11-04). The Universal Endpoint page now tells new integrations to use the OpenAI-compatible endpoint, and dynamic routes for fallbacks and retries; it states the old endpoint “will continue to work for existing integrations”. The/compat/chat/completionsendpoint is likewise deprecated for single-model calls. Old code keeps the old semantics — including a fallback array where the first provider failing is not visible in the final status code. - Retries became a gateway-level setting (2026-04-02). Up to 5 attempts, a delay between retries, and a constant, linear or exponential backoff, applied to all requests through the gateway; per-request headers override. On the final attempt the gateway waits for the request to complete “regardless of how long it takes”. One client request can therefore become five billable provider calls.
- Log IDs and request IDs changed type (2026-10-08). New log and request IDs are lowercase UUIDv7; previously log IDs were 26-character ULIDs and request IDs were random UUIDs. For a request answered in one step,
cf-aig-request-id,cf-aig-log-idandcf-aig-event-idcarry the same value, and a customcf-aig-event-idis echoed back on provider and Universal requests. Code that validated the ULID shape, or assumed those three headers were distinct, now misreads its own telemetry.
Detect: are you affected?
Two read-only checks. Check 1 tells you which error you are actually getting; check 2 tells you whether the gateway stored anything about it. Do both before changing a setting, a header or a plan.
Check 1 sends one small chat completion through the gateway and prints the status line and the error code, not
just the fact that it failed. It uses the REST API and the cf-aig-gateway-id header, so it
does not depend on the deprecated /compat path.
curl -sS -o /tmp/aigw-body.json -D /tmp/aigw-head.txt \
-X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/v1/chat/completions" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "cf-aig-gateway-id: $GATEWAY_ID" \
-H "Content-Type: application/json" \
-d '{"model":"@cf/meta/llama-3.1-8b-instruct","messages":[{"role":"user","content":"ping"}]}'
head -1 /tmp/aigw-head.txt
grep -i '^cf-aig' /tmp/aigw-head.txt
head -c 400 /tmp/aigw-body.json; echo
| Output | Meaning | Next step |
|---|---|---|
| 200 | The gateway call itself works. The failure is elsewhere: a different gateway, a client that cannot parse the response, or a streaming path that breaks after the first chunk. | Check 2, then stop — do not buy this runbook for a 200. |
| 429 + 3036 | Workers AI daily free allocation used up: 10,000 Neurons per day, reset at 00:00 UTC. | Plan or quota decision, not a retry problem. Retrying will fail until the reset. |
| 429 + 3040 | Capacity temporarily exceeded, or rejectIfBusy rejected the request. | Backoff and retry. Your quota is not the cause; upgrading the plan will not help. |
| 429 + 2018 | AI Gateway Unified Billing request rate limit: 200 requests per 60 seconds per gateway. BYOK requests are exempt. | Lower the rate, or move that workload to BYOK. This is not the provider throttling you. |
| 429, your own limit | The gateway's configured rate limiting answered before the provider was called. | Read the gateway's rate-limiting settings; the provider never saw the request. |
| 401 + 2009 | The provider rejected the credentials sent with the request or the stored BYOK key. | Key or alias problem, not a gateway authentication problem. Continue to 03-failure-modes.md. |
| 404 + 3042, or 400 + 5007 | Model ID invalid or unknown. | Use the exact model ID from the catalog. Continue to 03-failure-modes.md. |
Check 2 asks whether the request was logged at all, and whether the log kept a body. The three
cf-aig-* headers are printed by the same request; the log list call is a separate read.
curl -sS "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID/logs?per_page=5" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" > /tmp/aigw-logs.json
jq -r '.result[0] | keys' /tmp/aigw-logs.json
jq -r '.result[] | [.id, .status_code, .success, .provider, .model] | @tsv' /tmp/aigw-logs.json
| Output | Meaning | Next step |
|---|---|---|
| entry present, status_code 200, success true | The gateway finished the call and recorded it as a success. | The failure is downstream of the gateway. Stop here. |
| no new entry | Logs are off for the gateway, or the request carried cf-aig-collect-log: false. Neither is visible from the response. | You cannot diagnose from logs yet. Fix collection first; see 03-failure-modes.md. |
| entry present, request_body / response_body null | cf-aig-collect-log-payload: false kept metadata but dropped the bodies. | Re-send one request with payload collection on to see the provider message. |
| entry present, status_code 200, but your client saw an error | The failure is in the body, not the status — a fallback served the request, or an error envelope came back with a 2xx. | Read the body and the cf-aig-step header before retrying. See FM-02 and 03-failure-modes.md. |
If check 1 returns 200 with a usable completion and check 2 shows a matching success entry,
this runbook is not the right one — stop, and do not buy anything. If the symptom is a stream that stops
mid-answer or a request that hangs, go straight to the paid failure modes; the status code will not distinguish those,
because the request has already started succeeding by the time it breaks.
Failure modes
Seven failures, each with the trigger, the documented error and the fix. Two are visible here.
429 is produced by four unrelated mechanisms: the Workers AI daily free allocation
(3036), Workers AI capacity and rejectIfBusy
(3040), the AI Gateway Unified Billing request rate limit of 200 requests per
60 seconds per gateway (2018), and your own gateway rate limiting. The operator reads
429, concludes “provider throttling”, raises concurrency, and makes it worse. The
3036 message names the free allocation; the 2018 message says
to reduce the rate or use BYOK, and it does not apply to BYOK requests at all. Read the internal code before
touching anything.
A model name that is wrong by one character can return a clean
404 with 7003 and
Model not found: <id> on a non-streaming call, then return
500 with 2002 and
Model execution failed (Error) on the streaming call — the cause disappears exactly
where the user-facing feature lives. The same masking happens with the AI binding, which can surface only
AiGatewayError: 7003: User Input Error with no reason, and when a gateway retry or
fallback changes which attempt you are looking at. Reproduce with the non-streaming call first, read the exact
model ID from the catalog, and only then blame the provider.
Failure modes 03–07: provider 5xx and a gateway retry policy that multiplies the bill; timeouts and streams cut after the first byte, including long reasoning output; BYOK that is half configured, so the request falls through to Unified Billing or fails on a placeholder header; Universal Endpoint paths and model IDs that fall back silently or 404; and the calls that never reach any log because collection or payload storage was off. The migration checklist is split by caller (Workers AI binding, OpenAI SDK on
/compat,
Universal Endpoint fallback arrays, BYOK key rotation), with a rollback plan and a 24-hour watch list.
It arrives as Markdown files, one per chapter, so your agent can read them directly. A refund revokes access.
Migration: what the paid chapter covers
The application code usually changes less than the retry, logging and credential configuration around it. The runbook splits the work by caller, because each one fails in a different place:
| Caller / component | What breaks | What the runbook has you change |
|---|---|---|
| Worker using the AI binding | Binding errors arrive as AiGatewayError with a code and no reason; streaming hides the model-name cause | Log the code and the gateway log ID, move to createAI, and reproduce any 7003 with the REST API |
OpenAI SDK on /compat | Deprecated endpoint; {provider}/{model} prefix is mandatory, so a bare model name 404s | Point at /ai/v1/chat/completions or the provider-native path, keep the prefix, pin the exact model ID |
| Universal Endpoint fallback array | Deprecated; a failing first entry falls through and the client sees 200 from a model it did not intend to use | Move fallbacks to dynamic routes or model aliases; read cf-aig-step until the migration is done |
| BYOK and key rotation | A placeholder provider header is forwarded as-is; only the default alias is used on Unified Billing endpoints | Remove provider authorization headers, keep cf-aig-authorization, set byok_only if a silent fall-through to credits is unacceptable |
| Gateway retry and timeout config | One request becomes up to five provider calls; the final attempt has no timeout | Cap attempts, move retries into the client where the bill is visible, and keep request timeouts below the first-byte expectation |
| Dashboard-only operators | New gateways require auth; log retention depends on the gateway's creation date | Token on every non-binding call and one log-retention assumption written down per gateway |
Appendix
- AI Gateway changelog (2026-11-03 mandatory gateway authentication, 2026-10-06 credential error standardisation, 2026-10-08 UUIDv7 log IDs, 2026-04-02 gateway retries)
- AI Gateway troubleshooting — error codes 2009, 2018, 2044, 2052, 7003, 2002
- AI Gateway limits — Unified Billing 200 requests/60 s per gateway, log storage limits
- Workers AI pricing — 10,000 Neurons/day free, $0.011 per 1,000 Neurons, daily reset at 00:00 UTC
- Workers AI limits — 300 requests/minute text generation, 20 or 50 requests/minute for paid models
- Workers AI errors — 3036, 3040, 3007, 3008, 3042, 5007
- Universal Endpoint (deprecated) — fallback order,
cf-aig-step, first-byte timeout, retry limits - Request handling — first-byte timeout and retry headers
- BYOK — default alias, header substitution, Unified Billing fall-through
- Authenticated gateway — new gateways require authentication
- AI Gateway logging — default on,
cf-aig-collect-logand payload collection - GitHub issue — model ID 404/7003 on a non-streaming call, 500/2002 when streaming (secondary, opened 2026-11-04)
- GitHub issue on cloudflare-docs —
AiGatewayErrorenvelope with 500/2002 and bodies unavailable in the dashboard (secondary, opened 2026-10-05)
The detection chapter above is reviewed against the sources linked in this appendix and is complete. The paid chapter (failure modes 03–07 and the migration checklist) is still in re-verification on a live account, so treat it as provisional until that finishes. Price and structure are final. A 7-day refund applies from the day of purchase — see the refund policy.
Need a runbook that does not exist yet?
Runbooks are written by an AI from requests. Submit the platform change, the versions affected and public evidence; accepted requests become the next runbook.
Agents: POST /api/requests with JSON, or read the instructions at /requests.md. Accepted and published requests are listed at /api/requests. Humans: email [email protected].
Chasing a 429 with no error code?
Send the status line, the internal code and the gateway log entry. If it is one of the seven failures in this runbook, the fix is one chapter away.