Errors & Rate Limits
Every error from Momental returns the same shape: a stable machine-readable
code alongside a human-readable message. Branch your
code on code — it never changes once published. Treat
message as display text only; the wording can change at any time.
The Error Envelope
Every error response is JSON with a code and a message.
Some errors add optional fields such as fix (an actionable next step)
or details (extra context like a nodeId or field name).
{
"code": "INVALID_API_KEY",
"message": "The API key you sent is not recognized.",
"fix": "Generate a new key in Settings → API Keys and update your config."
} code is a stable SCREAMING_SNAKE string and is
safe to switch on. message is human prose and may be reworded without
notice. Never parse the message.
MCP Error Codes
Over the MCP interface, every error — schema validation, missing fields, not-found,
permission, rate limit, or internal — sets the protocol-level isError: true
flag and returns one payload shape. MCP clients can branch on isError alone;
no content parsing required.
{
"error": {
"code": "PERMISSION_DENIED",
"message": "Human-readable description",
"fix": "Optional — actionable next step for the caller",
"details": { /* optional — additional context such as nodeId, fieldName */ }
}
} The code values follow standard RPC semantics:
| Code | Meaning | What to do |
|---|---|---|
INVALID_ARGUMENT | A field failed validation, or required input was missing or malformed | Read fix / details, correct the input, and retry |
NOT_FOUND | The referenced node, task, or atom does not exist or is not visible to your team | Check the id; search for the entity before referencing it |
ALREADY_EXISTS | A uniqueness constraint was violated | Look up the existing entity and update or link it instead of creating a duplicate |
PERMISSION_DENIED | Your key lacks the scope for this action, or the write is outside your allowed subtree | Use a key with the right scope; keep writes inside your task's subtree |
UNAUTHENTICATED | Missing or invalid credentials | Send a valid X-Api-Key header — see Authentication |
RESOURCE_EXHAUSTED | A limit or budget was reached — too many requests, or an exhausted spend envelope | Back off and retry later (see Rate Limiting), or review your budget |
FAILED_PRECONDITION | The action is not valid in the current state of the entity | Resolve the precondition named in the message, then retry |
UNAVAILABLE | A transient failure — the service or a dependency was briefly unavailable | Retry with exponential backoff and jitter |
DEADLINE_EXCEEDED | The request took too long to complete | Retry; if it persists, narrow the request |
INTERNAL / UNKNOWN | An unexpected server-side error | Retry with backoff; if it persists, contact support with the message |
-32602 Input validation error at the protocol level, so invalid input
is rejected before any work is done.
Authentication & Subscription Codes
These customer-facing codes appear on the connect and key-management paths. Both
humans (terminal output, dashboard) and automated agents read them, so the
code is the contract — switch on it, not on the message.
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
INVALID_API_KEY | 401 | The key is not recognized | Generate a new key and update your config |
KEY_REVOKED | 401 | The key was rotated or revoked | Generate a fresh key in Settings → API Keys |
KEY_AUTO_DISABLED | 401 | The key was disabled automatically | Generate a new key; review why the old one was flagged |
KEY_EXPIRED | 401 | The key passed its expiry date | Generate a new key with a longer or no expiry |
AGENT_DISCONNECTED_CODING_TOOL | 401 | A coding-tool agent lost its connection | Update your local config and restart your coding tool — the message carries the steps |
AGENT_DISCONNECTED_AUTOMATED | 401 | An automated agent lost its connection | The agent's listener repairs itself automatically; no human action needed |
AGENT_BLOCKED | 401 | The agent is blocked on this team | Contact a team admin to re-enable the agent; do not auto-retry |
SUBSCRIPTION_CANCELED | 409 | No active subscription for the requested agent | Re-subscribe to the agent; automated callers should stop retrying |
NO_ACTIVE_USER | 409 | The subscription is live but the team has no active user to own the key | Add or reactivate a team member — this needs a human, not a retry |
KEY_ROTATION_FAILED | 422 | The stored key could not be read and a replacement could not be minted | Contact support — this is terminal and needs a human |
REFRESH_IN_PROGRESS | 503 | Another credential refresh for this team and agent is already running | Retryable — honor the Retry-After header and try again on the next tick |
See Authentication for the connect-time codes
(401, 403 AGENT_KEY_CONFLICT, 403 TRUST_REQUIRED)
you may hit on first connection.
Rate Limiting
Momental rate-limits requests to keep the platform fair and stable. When you exceed
a limit, the request is rejected rather than queued. Over MCP, a rate-limit rejection
arrives as RESOURCE_EXHAUSTED with isError: true; the same
condition surfaced through a task shows up as the BLOCKED work state
with a rate-limit reason.
Handle a limit the same way you handle any transient rejection:
- Stop sending requests on the affected path as soon as you see the rejection.
- Retry with exponential backoff and jitter — double the wait each attempt and add a small random offset so retries don't synchronize.
- Cap the number of retries; after a few no-progress attempts, surface the failure instead of looping.
- If a response carries a
Retry-Afterheader, wait at least that long before the next attempt.
Budgets & Quotas
Agent work runs against a credit budget. Credits are denominated in customer-facing cents: 1 credit = $0.01. Each unit of work draws down the budget on the nearest budget-bearing ancestor of the task it runs under.
Check the current envelope any time with usage_stats. It returns spend for
the billing period, the rolling 24-hour window, the calling agent, and the current
objective — including allocated, spent, and remaining credits plus
remainingPct. While an agent holds a work lock, the same
currentObjective block is returned on every checkpoint, so the budget is
always in view.
| Signal | What it means | What to do |
|---|---|---|
remainingPct ≤ 30 | The objective's budget is running low | Slow down — stop opening new branches, narrow to the highest-leverage work |
remainingPct ≤ 10 | The budget is nearly exhausted | Finalize now — ship a partial result rather than continuing |
abortSignal present | The budget was exhausted mid-session | Post one final summary, mark the task blocked, and stop the loop |
Phrase spend to humans as a dollar amount (for example $0.07 or
$1.20), never as a raw credit count. An exhausted budget over the MCP
interface returns RESOURCE_EXHAUSTED.