Skip to content
Version: Current

Diagnose a 502

A 502 means every candidate seat failed. The gateway does not retry the same deployment, so the request has a small, ordered list of reasons.

Terminal window
curl -sS -o /tmp/reply.json -w '%{http_code}\n' http://127.0.0.1:4020/v1/chat/completions \
-H "Authorization: Bearer $ACUMEN_KEY" -H 'Content-Type: application/json' \
-d '{"model":"cheap","messages":[{"role":"user","content":"ping"}]}'
jq -r '.code // .error.code // .error.type' /tmp/reply.json

upstream_error is the vendors; upstream_empty is a vendor that answered 200 with no content; model_no_eligible_seat is a routing failure before any vendor was called. The full set is in the error reference.

model_no_eligible_seat and model_not_found are decided before a call. If you see either, no vendor was contacted and the seat list is the problem. Check seat_health for a cooling row:

SELECT provider, account, model, cooling_until, consecutive_failures, last_error_code
FROM seat_health
WHERE cooling_until > now()
ORDER BY cooling_until DESC;

Every seat attempt is one row in attempt (internal/store/migrations/0002_ledger.sql:25), joined to its request. The newest failures for one request answer “who did we try, in what order”:

SELECT a.seat_id, a.status, a.error_code, a.latency_ms
FROM attempt a
WHERE a.request_id = $1
ORDER BY a.id;

A seat that fails repeatedly cools down rather than disappearing. Do not delete the row: add a provider again with a corrected key, or retire the offer, and the cooldown resolves on its own.

That is an estate problem, not a request problem: check the provider’s status page and the gateway’s own metrics before changing anything. A wrong key on one seat cannot produce a fleet-wide 502; the routing has other candidates. First separate the two: the status page probes the gateway at GET /readyz, so an ai row that is up means the estate answered and every upstream failed.