Skip to content
looot docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

REST errors

Every HTTP status and code the looot REST API answers with, and the fix for each.

Every REST error is a JSON body {error: {code, message, requestId}} plus an HTTP status. Some routes add fields next to error; those are noted below. See Errors for the shape and Idempotency and Fallback for background on a few of these.

validation_error

400. The request body or query failed schema validation. Retryable: no. Fix: read the message, which names the field; check it against the endpoint’s schema with inspect or GET /v1/operations/{id}. See inspect.

unauthorized

401. The bearer token is missing or invalid. Retryable: no. Fix: sign in again (looot login, or set LOOOT_TOKEN). See Sign in.

token_revoked

401. This token was revoked, or its login approval was denied. Retryable: no. Fix: run looot login again, or an MCP client should reopen its sign-in flow. See Sign in.

forbidden

403. The token lacks the scope this route requires. Retryable: no. Fix: looot whoami (or the MCP client’s own sign-in) lists its scopes; an owner or admin can issue a token, or approve a sign-in, with the scope you need. See Access.

insufficient_balance

402. POST /v1/runs when the reservation would exceed the workspace’s available balance. Body adds balanceMicros, estimatedCostMicros, topUpUrl, and topUp: {minimumUsd, suggestedUsd, checkoutUrl, dashboardUrl, message}. This is a pre-admission refusal: no hold is placed and nothing is charged. Retryable: no. Fix: show topUp.checkoutUrl (Stripe’s hosted page) or topUp.dashboardUrl to whoever holds the account, then retry with a new idempotencyKey once paid. See Before your first run and Top up.

not_found

404. Unknown runId, connectionId, or catalog item. This is not what you get for an unknown endpointId on POST /v1/runs, though: that answers 201 with status: "failed" and a populated error instead. See Errors: failed runs. Retryable: no. See runs_list.

route_not_found

404. There’s no route at this method and path at all (a typo in the path, or a route this gateway doesn’t serve). Retryable: no. Fix: check the path against the API reference.

method_not_allowed

405. The route exists, but not for this HTTP method. Retryable: no. Fix: check the method against the API reference.

idempotency_conflict

409. The idempotencyKey was already used with a different endpointId or input than the first call that used it. Retryable: no. Fix: send the exact same input to replay the original run, or use a new key for a new one. See Idempotency.

rows_mode_unsupported

422. The request needs a fleet capability this gateway’s storage mode doesn’t provide right now (for example output.mode: "canonical", which production doesn’t serve). Body adds reason. Retryable: no. Fix: drop the unsupported option, or use output.mode: "raw". See inspect.

too_many_inflight_runs

429. POST /v1/runs when this workspace already has limit runs in flight (8 by default). Body adds inflight, limit, retryAfterSeconds, and a retry-after header. Retryable: yes. Fix: wait for some runs to finish, then retry with the same idempotency key. See Idempotency.

rate_limit_exceeded

429. A token-bucket limiter rejected the request (60 burst, 10 per second per tenant by default). Headers: x-ratelimit-limit, x-ratelimit-remaining, retry-after. Retryable: yes. Fix: wait the retry-after time, then retry.

runs_cursor_invalid

400. The cursor sent to GET /v1/runs (or runs_list) is invalid or stale. Retryable: no. Fix: start paging again from runs_list/GET /v1/runs with no cursor. See runs_list.

unknown_category

404. GET /v1/catalog/overview?category=... (or MCP catalog_overview, or looot catalog overview --category) named a category id that doesn’t exist. Body adds field and categories, the valid ids. Retryable: no. See catalog_overview.

unknown_platform

404. GET /v1/catalog/overview?platform=... named a platform id that doesn’t exist, or one outside the given category. Body adds field. Retryable: no. See catalog_overview.

internal_error

500. An unhandled error on looot’s side. Retryable: usually yes. Fix: retry once; if it persists, report it with the requestId. See Support.

catalog_loading

503. The catalog hasn’t finished loading in this gateway process yet. Retry with the retry-after header. Retryable: yes.

storage_busy

503. The storage backend is briefly overloaded. Body adds retryAfterSeconds, and a retry-after header is sent too. Retryable: yes. Fix: wait a few seconds and retry with the same idempotency key. See Idempotency.

storage_uncertain

503. Service storage couldn’t confirm the last write. Retryable: yes. Fix: wait briefly, then retry with the same idempotency key; this never means the write actually failed, only that this node can’t yet confirm it landed. See Idempotency.

billing_unavailable

503. A billing route was called, but billing isn’t enabled on this gateway. Retryable: no. See Support.

runtime_bridge_timeout

503. The storage bridge didn’t respond in time. Body adds retryAfterSeconds, and a retry-after header is sent too. Retryable: yes.

Settlement outcomes, not HTTP errors

Once a run is admitted (past every refusal above), its money outcome is never a blanket “the HTTP call failed”: a definitive failure settles at $0 or the provider’s evidenced cost with the hold released, and an uncertain outcome parks at status: "reconciliation_pending" with the hold kept until it is reviewed. Check status and error on the run, not the HTTP status, to see which applies; see Errors: failed runs and Run errors.

Was this page helpful?