-
Notifications
You must be signed in to change notification settings - Fork 0
Error Codes
Every denial carries an HTTP status, a stable type URN, the deciding gate, and the requestId. This page is the catalogue: what each code means, why it fired, and what to do about it.
Errors have two origins — do not conflate them:
-
Gate denials — NornGate stopped the request at a gate. These are the codes below, always fail-closed, always in
application/problem+json(RFC 9457) with agatefield. -
Upstream passthrough — on
ALLOW, the target service's own response flows back unchanged. A404or422from your service is not a gate denial and carries nogatefield; look to your service, not the gates.
{
"type": "urn:norngate:error:policy-denied",
"title": "Policy denied",
"status": 403,
"detail": "agent role cannot access resource type 'model'",
"gate": "policy",
"requestId": "req_abc123"
}
The presence of gate is the tell: if it is there, a NornGate gate denied you; if not, it is your upstream.
Two failure modes: a denial (the gate evaluated and refused) and a timeout (the gate could not decide in its budget; circuit breaker trips). Both fail-closed.
| Code | type (urn:norngate:error:…) | Gate | Mode | Retryable |
|---|---|---|---|---|
| 400 | malformed-request | G0 | denial | no |
| 401 | unauthenticated | G0 | denial | after re-auth |
| 403 | policy-denied / approval-denied | G1 / G2 | denial | no (as-is) |
| 408 | approval-timeout | G2 | timeout | conditional |
| 409 | write-conflict | G4 | denial + timeout | yes (backoff) |
| 429 | rate-limited | G0 | denial | yes (Retry-After) |
| 500 | sandbox-failure | G3 | denial + timeout | only if transient |
| 503 | gate-unavailable | G0 / G1 | timeout | yes (backoff) |
References. RFC 9457 (Problem Details for HTTP APIs); RFC 9110 (HTTP semantics — status codes, Retry-After). Naming doctrine: Prose Edda / Poetic Edda, per Norse Cosmology & Platform Design.
Every denial carries an HTTP status, a stable type URN, the deciding gate, and the requestId. This page is the catalogue: what each code means, why it fired, and what to do about it.
Errors have two origins — do not conflate them:
-
Gate denials — NornGate stopped the request at a gate. These are the codes below, always fail-closed, always in
application/problem+json(RFC 9457) with agatefield. -
Upstream passthrough — on
ALLOW, the target service's own response flows back unchanged. A404or422from your service is not a gate denial and carries nogatefield; look to your service, not the gates.
{
"type": "urn:norngate:error:policy-denied",
"title": "Policy denied",
"status": 403,
"detail": "agent role cannot access resource type 'model'",
"gate": "policy",
"requestId": "req_abc123"
}The presence of gate is the tell: if it is there, a NornGate gate denied you; if not, it is your upstream.
Two failure modes: a denial (the gate evaluated and refused) and a timeout (the gate could not decide in its budget; circuit breaker trips). Both fail-closed.
| Code |
type (urn:norngate:error:…) |
Gate | Mode | Retryable |
|---|---|---|---|---|
400 |
malformed-request |
G0 | denial | no |
401 |
unauthenticated |
G0 | denial | after re-auth |
403 |
policy-denied / approval-denied
|
G1 / G2 | denial | no (as-is) |
408 |
approval-timeout |
G2 | timeout | conditional |
409 |
write-conflict |
G4 | denial + timeout | yes (backoff) |
429 |
rate-limited |
G0 | denial | yes (Retry-After) |
500 |
sandbox-failure |
G3 | denial + timeout | only if transient |
503 |
gate-unavailable |
G0 / G1 | timeout | yes (backoff) |
| Code | Cause | Remedy |
|---|---|---|
400 |
Missing fields, bad schema, oversized body | Fix the request shape; validate before sending. |
401 |
No valid SPIFFE SVID (mesh) or bearer token/API key (external) | Re-authenticate; check the SVID is issued and unexpired, or refresh the token. |
403 |
No allow rule matched, a deny rule matched (deny-overrides), or required approval was refused |
Adjust policy for the (identity, resource, action) tuple, or obtain the approval; inspect the record to see which rule fired. |
408 |
A human approval was required and no decision arrived within 30s | Re-submit with the same Idempotency-Key; the approver may decide on a later attempt. Consider a standing automated-consent policy to cut human transits. |
409 |
Another writer holds the resource lease, or the G3 fencing token was stale at commit | Retry with backoff and the same Idempotency-Key; the lease frees or the state is re-read. |
429 |
Per-identity/tenant rate limit exhausted at G0 | Honor Retry-After; smooth request rate; request a higher limit if legitimate. |
500 |
Sandbox validation failed, a disallowed effect was produced, or VM allocation failed | If the effect was disallowed, fix the action (do not retry blindly). If it was a transient allocation failure, retry with backoff. |
503 |
A gate (policy store, engine) could not respond within its budget | Retry with backoff; this is a transient dependency outage — the fleet is failing closed, not failing open. |
-
Always retry
503and409with exponential backoff;429per itsRetry-Afterheader. -
Conditionally retry
408(a human may approve next time) and500(only if the cause was transient allocation, not a disallowed effect). -
Do not retry
400,401, or403unchanged — the input, identity, or policy must change first. -
Retries are safe for any request carrying an
Idempotency-Key: the side effect commits exactly once at G4 regardless of delivery count ([Deterministic Execution](Deterministic-Execution)). Never retry a state-changing request without one.
A denial is a queryable fact, not a dead end. Take the requestId and resolve it:
GET /v1/audit?requestId=req_abc123The decision record names the deciding gate, the matched rules (by reference), and the pinned policy / state / config snapshot IDs. From there, norngate-cli audit replay req_abc123 re-derives the verdict against those exact inputs — so "why was this denied?" always has a reproducible answer. Record shape: [Urd Ledger](Urd-Ledger).
- [API Reference](API-Reference) — the problem+json contract and headers.
- [The Five Gates](The-Five-Gates) — what each gate checks and why it denies.
- [Urd Ledger](Urd-Ledger) — the record behind every denial.
- [Reference](Reference) — quick-reference tables.
Section glyphs are Elder Futhark runes (Unicode Runic block, U+16A0–U+16FF) — semantic, not emoji. Full set on [Home](Home#iconography):
| Rune | Name | Gloss | Marks |
|---|---|---|---|
| ᚾ | Nauðiz | need, constraint | the platform mark |
| ᚺ | Hagalaz | hail, failure states | overview |
| ᛁ | Isa | ice, the fixed catalogue | the codes |
| ᛒ | Berkanan | growth, remedy | cause & remedy |
| ᛖ | Ehwaz | the journey retraced | retry policy |
| ᚲ | Kenaz | the torch, looking into | reading a denial |
| ᚱ | Raidō | the ride, the road | next steps |
References. [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (Problem Details for HTTP APIs); [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110) (HTTP semantics — status codes, Retry-After). Naming doctrine: Prose Edda / Poetic Edda, per [Norse Cosmology & Platform Design](Norse-Cosmology-and-Platform-Design).
Welcome
Getting Started
Core Concepts
Guides
- Guides
- Configuring Gates
- Setting Up Subdomains (Realms)
- Integrating AI Agents
- Monitoring & Observability
- Disaster Recovery (Ragnarök Drill)
- Custom Domains & TLS
Reference
Silence means no.