Errors & problem+json
The Management API error contract — RFC 9457 problem+json with a registered type, a stable code, and a request_id.
On this page
Every non-2xx from the Management API is application/problem+json (RFC 9457) with a
registered type, a stable machine-readable code, and a request_id that matches the
TT-Request-Id header carried on every response.
For symptom-first fixes keyed to what you’d actually search — 401 storms, UNPRICED rows,
silent routing — see Troubleshooting.
The envelope
type— a relative on-host path (air-gap-dereferenceable), documented at/tt/api/problems/<slug>; the full registry is served at/tt/api/v1/problems.code— a stable string code.request_id— matches theTT-Request-Idresponse header, for correlation with the ledger and the audit trail.
Framework problems
These apply to every operation and are documented once (they’re each operation’s default response):
| Status | Problem |
|---|---|
| 400 | bad_request |
| 401 | unauthenticated |
| 403 | forbidden (names the required_scope in the body and the TT-Required-Scope header) |
| 404 | not_found |
| 405 | method_not_allowed |
| 413 | payload_too_large |
| 429 | rate_limited (with Retry-After) |
| 500 | internal |
Special status codes worth knowing
412 precondition_failed— a config compare-and-swap lost the race; refetch the generation and retry (If-Match).422 restart_required— a restart-only config change (e.g. awebhooks:edit) was submitted; nothing was written (previews still return200). Also422 unsupported_override_classwhen overriding a rate/concurrency limit per node.410 gone— a sunset operation (names its successor), or410 cursor_expiredon the events feed (carriesoldest_seqso you can reconcile from the audit trail).503 enforcement_unavailable— afail: closedgovernance limit whose guarantee is uncheckable (never a fake 429; see Governance).
Guardrail boot refusals
These are not HTTP statuses. A malformed guardrail policy refuses at boot — the process does not start — so that no row is ever written carrying a fallback disclosure for a policy that should not exist. The three you are most likely to meet:
| What you wrote | The refusal |
|---|---|
| A phase a detector does not declare | binding "code_out" binds detector "code.patterns" to phase "stream", which it does not declare (declared: [output]) |
on_error: closed with no ack |
binding "pii_in": on_error 'closed' requires fail_closed_ack — a dead detector becoming an outage is a decision an operator must type out |
A judge or sidecar detector with no consent |
guardrails.judge.external: … It requires external: allow — the air-gap gate. Without it this tier is REFUSED |
Also refused: mode: simulate in a binding (it is the dry-run report’s stamp, never a policy), and
stream_fallback: closed without the same fail_closed_ack. Stock activation also rejects empty
detector lists, rewrite, reask, stream_fallback: buffer, sidecar bindings, raw-required external
detectors and spanless mutation combinations. Positive judge hard caps require a proven
conservative reservation price; an explicit zero cap denies every call. See
Configuring a guard.
Governance refusal reasons
Governance refusals map a reason to a status: invalid_key → 401, scope_violation →
403, budget_exceeded / quota_exhausted / rate_limited / concurrency_exceeded → 429,
enforcement_unavailable → 503. Each is written row == wire — the HTTP response and the
decision-log row are stamped from the same value.
The full catalog, and the type registry, are in the rendered API reference.