Error Envelope

JSON-API-inspired error format. Stable machine-readable codes. Source pointers for validation.

Shape

type ErrorResponse = {
  errors: Error[]
}

type Error = {
  code: string                   // machine-readable; stable identifier
  title: string                  // human-readable summary
  detail?: string                // specific message for this instance
  source?: {
    pointer?: string             // JSON Pointer to the field in the request body
    parameter?: string           // query string parameter name
    header?: string              // request header name
  }
  meta?: Record<string, unknown>  // anything extra (e.g., row count, correlation_id)
}

Common codes

CodeHTTPMeaning
unauthorized401Token missing, expired, or unrecognized
scope_missing401Token doesn't carry the required scope
forbidden403Auth'd, but the action isn't allowed (resource-level)
override_required403Endpoint requires X-Operator-Override flag
bulk_override_required403Operation exceeds bulk threshold; include bulk in override
not_found404Resource doesn't exist or not visible to the token
idempotency_conflict409Same key + endpoint, different body
cursor_invalid410Cursor's sort/filter signature no longer matches
validation_failed422Request body doesn't satisfy schema
cache_warming202Insight cache is computing; retry per Retry-After
rate_limited429Request rate exceeded; see Retry-After and RateLimit-* headers
internal_error500Unhandled server failure; correlation ID in meta.correlation_id

Multiple errors per response

Validation failures return multiple errors entries — one per field. Other errors are typically single.

{
  "errors": [
    {
      "code": "validation_failed",
      "title": "Field is required",
      "detail": "name must be present",
      "source": { "pointer": "/data/attributes/name" }
    },
    {
      "code": "validation_failed",
      "title": "Invalid format",
      "detail": "email must be a valid email address",
      "source": { "pointer": "/data/attributes/email" }
    }
  ]
}

Correlation IDs on 5xx

Every 5xx response carries a meta.correlation_id matching gondor's request log. Forward it to support to trace a specific failed request.

{
  "errors": [
    {
      "code": "internal_error",
      "title": "Server error",
      "detail": "Please contact support with the correlation_id",
      "meta": { "correlation_id": "01H8XK3J5Z9M2P4Q6R8S0T2V4W" }
    }
  ]
}

Why machine-readable codes

The code field is the contract. title and detail are subject to copy changes; the code stays stable across versions. Switch on code in your handler, never the HTTP status alone (multiple codes share statuses — see unauthorized + scope_missing both at 401).