401 Unauthorized on /v1 requests
Your relay key is missing, malformed, or wrong.
- Confirm the header is exactly
Authorization: Bearer <relay-key>. - Use the plaintext returned when the key was created, not the id or prefix. If you lost it, mint a new one — plaintext is never retrievable.
- A disabled relay key also returns 401; check it’s enabled in the dashboard.
403 Forbidden — model not granted
The relay key authenticated, but its policy doesn’t grant the requested
model.
- In the dashboard, open the policy attached to your relay key and confirm the model is in its allowed set.
- The model name in your request must match a catalog model the policy grants.
404 — unknown model or no binding
- The
modelvalue doesn’t resolve to any catalog model, or - the model has no enabled host binding. A model needs a binding to a host (with an adapter and an upstream name) to be routable.
502 / 503 — “no healthy keys in pool”
Every host key for the route has tripped its circuit breaker — usually
because the upstream credential is wrong, revoked, or rate-limited. Each key
has a per-key breaker keyed on auth / quota / rate-limit / server-error
classification.
To recover:
- Fix the underlying cause — correct the upstream key value, or wait out a rate limit.
-
Clear the breaker state so the fixed key can be tried again. In dev with
the bundled stack:
This clears the
secret_health:*keys in the state backend. In production, breakers heal automatically once the cooldown passes.
A rotated key gets a fresh breaker automatically — the breaker is keyed by
value-hash, so a new value starts clean and the old hash’s record expires.
Adapter mismatch
If a request to a model returns a wire-format error from the upstream, the host binding’sadapter (openai or anthropic) likely doesn’t match the
upstream’s actual wire protocol.
- A single host (e.g. AWS Bedrock) can serve models on different protocols — the adapter is set per binding, not per host.
- Confirm the binding’s adapter matches how that specific model speaks
(Claude on Bedrock →
anthropic; Llama on Bedrock →openai).
The container starts but the dashboard won’t log in
- On the standalone image the admin password is auto-generated and
printed to the container logs on first boot — check
docker logsfor theadmin/ password line. It’s persisted on the data volume across restarts. - The admin UI is served on
RELAY_CONTROL_PORT(:8081by default; standalone maps it there). Set it tooffonly if you want the control plane disabled. - For local HTTP (no TLS),
RELAY_COOKIE_SECUREmust befalse— otherwise the session cookie is markedSecureand the browser drops it. The standalone image already defaults it tofalse; set it on the lean image. - On the lean image you supply credentials yourself: set
RELAY_ADMIN_TOKENfor bearer access, or mount akind: UserYAML intoRELAY_CONFIG_DIRfor dashboard username/password login.
Credentials (and catalog) regenerate on every run
You’re starting a new container each time without a volume, so the standalone image boots against an empty data dir and regenerates the master key, admin credentials, and catalog. Mount a named volume to persist them:docker stop + docker start <container> already preserves state; only a
fresh docker run without a volume starts clean.
Stored host key won’t decrypt after a restart
RELAY_MASTER_KEY changed between runs. It must be stable — it’s the key
that decrypts stored secrets. Restore the original master key, or re-enter
the host key values under the new one. The standalone image keeps it stable
automatically as long as you mount a volume (see above).
Health checks
Still stuck? Send the request, the full error body, and the output of
/healthz and /version — that’s enough to diagnose almost anything.