Errors
Error codes, HTTP statuses, deny reasons and validation fields, the same in Go, TypeScript and the HTTP API.
Codes
| Code | HTTP | Title | When |
|---|---|---|---|
denied | 403 | Forbidden | A decision denied; reason says why. |
escalation | 403 | Not allowed to grant this | An admin action beyond the caller's reach, or assigning themselves. |
invalid | 400 | Invalid request | Bad input. Validation problems name the field. In strict mode, also unknown permissions and invalid resources. |
not_found | 404 | Not found | |
conflict | 409 | Conflict | The last access admin, an already revoked assignment, a role in use, a duplicate key or mapping. |
unavailable | 503 | Authorization unavailable | Cerbos, the store or the resolver failed. Checks treat it as a denial. |
Anything else a handler passes to accesshttp.WriteError becomes a 500 with no detail; internals never reach the response.
Deny reasons
Set on denied. Record checks report no_grant or policy; Explain (reason on the Explanation) reports the finer other_org, scope, condition and org_rule.
| Reason | |
|---|---|
no_grant | They hold the permission at no scope. |
policy | Cerbos denied this record. |
other_org | The record belongs to another org. |
scope | Held, but the record is outside every scope it's held at. |
condition | In scope, but the action's condition is false. |
org_rule | An org rule denied it; Explain names the rule. |
unknown_permission | Not in the manifest. |
invalid_resource | Missing or extra attributes, a wrong type, a value outside an enum, the wrong kind. |
unavailable | An outage, turned into a denial by Assert / assert. |
no_subject | No subject on the request. |
Nest adds two of its own: 403 undeclared_route for a route with no access decorator, and 500 unchecked_requirement (both SDKs, strict mode) for a guarded route that never checked its permission against a record.
Validation fields
Admin validation problems carry field, the request field they're about, so forms show them at the input: userId, roleId, scopeType, scopeIds, validFrom, validUntil, reason, key, name, permissions, groupName.
In code
var e *access.Error // Code, Reason, Permission, Field, Message, CallID
if errors.As(err, &e) && e.Code == access.CodeDenied { ... }
access.ErrorCode(err) // the Code, or "" for other errors
access.IsDenied(err) // a denial?
accesshttp.WriteError(w, err)import { AccessError } from "@zynactra/access";
if (err instanceof AccessError && err.code === "denied") { ... } // code, reason, permission, field
throw new AccessError({ code: "not_found", message: "leave request not found" });In React, the client throws AccessApiError with status, code, reason, permission, field and the full problem.
An access error is a denial: never catch one and continue.