Brume uses structured JSON error bodies in HTTP responses. This page is the reference for every error shape, what causes it, and the action that resolves it.
Non-2xx responses carry a JSON body:
An over-limit evaluation returns HTTP 200 with success: false in the body — Upstash-style semantics. Your application branches on success, not on status codes:
The only 429 the gateway itself emits is PLAN_LIMIT (below).
Emitted when the project hits a per-axis tier cap. Status 429, body:
reason is a closed set you can pattern-match on:
| Reason | Meaning | Resolution |
|---|---|---|
rate_limit_req_day_cap_reached | The tier's daily check budget is exhausted | Upgrade the tier; the new cap applies immediately |
rate_limit_rules_cap_reached | Cannot create another rule | Delete unused rules or upgrade |
rate_limit_namespaces_cap_reached | Cannot create another namespace | Consolidate or upgrade |
rate_limit_overrides_cap_reached | Cannot add another override to the rule | Prune overrides or upgrade |
rate_limit_quotas_cap_reached | Cannot create another quota | Upgrade |
rate_limit_blocklist_cap_reached | Block/allow list is at capacity | Prune entries or upgrade |
webhooks_cap_reached | Cannot register another webhook | Upgrade |
projects_cap_reached | Cannot create another project | Upgrade the workspace |
seats_cap_reached | Cannot invite another member | Upgrade the workspace |
upgradeTier names the next tier that lifts the axis, or is null when you are already on Business.
| Status | Cause | Resolution |
|---|---|---|
401 | Missing, malformed, revoked, or unknown key | Check the Authorization header; re-mint the key |
403 | Key valid but missing the required scope | Mint a key with the right scope (rate_limit, manage_project, read_stats) |
400 with a message naming the offending field — a bad algorithm name, a zero limit, a namespace over 128 characters, a cost above 10,000. Fix the request; nothing was evaluated.
When the rate-limit Redis is unreachable, the gateway fails open: the evaluation returns HTTP 200 with success: true and degraded: true, and the X-Brume-RateLimit-Degraded: true header is set. Traffic keeps moving; the check is simply unaccounted.
The SDK mirrors this with two knobs:
timeout: { ms, fallback: 'allow' | 'deny' } — what to do when the gateway is slowonError: 'allow' | 'deny' — what to do on network failureBoth return a synthetic result with degraded: true and window: 'timeout' or 'error', so your code path is identical whether the degradation happened at Redis or on the wire. Choose allow for availability, deny for correctness.
PLAN_LIMIT: do not retry; upgrade or wait for the daily reset.401/403: do not retry; fix credentials.5xx or transport failure: retry with backoff, honoring your timeout/onError policy.