Open app
Moonborn — Developers

Errors

Error envelope shape, full error code catalog, retry semantics, and rate-limit-specific handling.

Every Moonborn error response shares the same envelope:

{
  "error": {
    "code": "rate_limited",
    "message": "Per-minute API rate limit exceeded.",
    "details": { "retryAfter": 12 }
  }
}

code is a stable, machine-readable string. message is human-readable (safe to show in logs; do not show to end users without translation). details carries code-specific fields when relevant.

HTTP status code mapping

StatusWhen
400malformed request (parse error, missing required field)
401missing or invalid bearer token
403authenticated but unauthorized (scope or role insufficient)
404resource not found
409conflict — typically idempotency key collision, drift block, or persona state machine refusal
422validation error (shape valid, semantics not)
429rate-limited
500unexpected server error
502/503/504upstream LLM provider issue; retry

Error code catalog

CodeStatusMeaning
unauthorized401Bearer token missing or expired
forbidden403Token valid, insufficient scope/role
not_found404Resource doesn't exist or isn't visible to caller
validation_failed422Request body shape valid but value range / enum / required-field mismatch
idempotency_conflict409Same idempotency key, different payload
state_conflict409Resource state machine refused the transition (e.g. archive an already-deleted persona)
drift_blocked409Drift detection action=block tripped on this reply
audit_blocked409Audit verdict below threshold; persona returned in flagged state
moderation_blocked422Output failed moderation; reply refused
rate_limited429Per-tier rate cap hit; details.retryAfter (seconds) carries the cooldown
quota_exceeded429Monthly quota hit (e.g. generation quota)
provider_error502Upstream LLM provider failure; retry with backoff
provider_timeout504Upstream LLM provider timed out
internal_error500Unhandled exception; logged on our side

Retry semantics

The SDKs auto-retry these codes with exponential backoff:

  • provider_error (5xx)
  • provider_timeout (504)
  • rate_limited (429) — honors Retry-After header

These codes are never auto-retried:

  • validation_failed — fix the request first
  • idempotency_conflict — change the key
  • drift_blocked / audit_blocked / moderation_blocked — caller decision

Idempotency-Key

Every write request accepts an Idempotency-Key header. Replays within 24 hours return the original response. SDKs auto-generate the key by default; override per-call if you have a deterministic business key (Idempotency-Key: order-12345).

Rate-limit headers

Every response (success or error) carries:

X-RateLimit-Limit: 3000
X-RateLimit-Remaining: 2987
X-RateLimit-Reset: 1747498200

Use X-RateLimit-Remaining for backpressure in your client. See Rate limits for per-tier caps.

Honest scope

We track the codes above; we do not surface internal exception types, stack traces, or upstream provider error codes. If you're debugging a provider_error, the audit log (GET /v1/audit/events) carries the upstream error envelope under details.