Skip to content

API error code reference

Every NovaVMS API error response wraps the code + message inside an error object. The code field is the stable contract — match against it, not the HTTP status alone (statuses overlap across causes).

Envelope shape

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable detail",
"details": { "field": "email", "reason": "missing" }
}
}
  • code — UPPERCASE_SNAKE constant. Stable contract.
  • message — human-readable; subject to change between releases.
  • details — optional structured per-error fields (validation field names, conflict targets, etc.). Omitted for most cases.

Source: cloud/pkg/apierror/*.go.

There is no request_id field in the v1 response envelope. Server-side log correlation uses the X-Request-Id header if you set one on the request (the server echoes it back); the body does not surface it.

4xx — client errors

CodeHTTPMeaningHow to fix
VALIDATION_ERROR400Request body failed validation. details lists the failing field where available.Fix the input per the endpoint’s reference page.
UNAUTHORIZED401Missing, malformed, expired, or signed-by-different-key token. Also returned when refresh-token user-agent mismatch (D-R2).Re-authenticate. For JWTs, refresh first; for service-account keys, confirm the sa_ prefix and that the SA has not been disabled.
FORBIDDEN403Authenticated but role insufficient for the operation. Also returned when an admin disables impersonation (ORG_INACTIVE-like 7-day deletion grace returns this).Check the caller’s role + the target resource’s org.
ACCOUNT_DISABLED403The user row has enabled=false.Re-enable from /admin/users.
ACCOUNT_LOCKED423Login lockout (5 consecutive failed logins, 15-min window).Wait 15 min or have an admin reset.
NOT_FOUND404Resource not in caller’s org (or never existed). v1 returns 404 (not 403) for cross-org access to avoid leaking existence.Confirm ID + org ownership.
CONFLICT409State conflict (duplicate email on create, etc.).Re-fetch the resource and retry.
GONE410Pairing code or shared-clip token expired.Mint a new pairing code or share token.
LIMIT_EXCEEDED422Server-side limit hit (max sessions per user, max alert-rule recipients, etc.).Reduce the input or raise the org-level limit.
UNPROCESSABLE422Semantic validation failure (well-formed but business-rule-violating).Read message.
REQUEST_TOO_LARGE413Payload exceeds limit (e.g. CSV export filter spans > EVENT_EXPORT_MAX_ROWS).Narrow the filter or paginate.
RATE_LIMITED429SA caller exceeded the 600/min bucket (or per-camera 1/5s trigger limit).Back off; see Rate limit handling. Note: no Retry-After header is set in v1 — back off with exponential jitter.

5xx — server errors

CodeHTTPMeaningHow to fix
INTERNAL_ERROR500Unhandled server-side error.Retry with exponential backoff. If persistent, capture the response + your request URL/time and forward to support.
BAD_GATEWAY502Webhook delivery sub-status — outbound delivery target returned non-2xx or timed out. Not relevant to inbound API calls.See Webhook delivery failures.
AI_PROVIDER_UNAVAILABLE503The AI runtime / Gemini judge service is temporarily unreachable.Retry; check the AI runtime health card at /admin/ai.

Where the codes are wired

The full source-of-truth mapping is in apierror/errors.go (search the repo for errMap). If a code appears in a response that isn’t in this table, file a docs bug.

See also