---
title: MCP errors
description: Every code a looot MCP tool call can return as isError, whatever tool or layer produced it, and the fix.
sidebar:
  label: MCP
---

Every tool error is `isError: true` with `structuredContent.data` shaped
`{code, message, retryable, requestId, error, ...context}` (`context` can add fields like
`tool`, `runId` or `endpointId`). See [Errors: MCP tool errors](/errors#mcp-tool-errors) for the
shape. `message` always says what to do next; read it before matching on `code` alone.

Three layers produce this same shape: a tool handler's own refusal, the SDK's own input-schema
validation before a handler ever runs, and a catch-all boundary around every handler that turns an
unexpected throw into [`internal_error`](/errors/rest-errors#internal_error).

### validation_error

Arguments didn't match the tool's input schema, or an application-level check inside the handler
rejected them. Retryable: no. Fix: check each field against `tools/list`, fix the named one, and
call again. See [`inspect`](/mcp-tools/inspect).

### unknown_tool

The tool name isn't one this server has, or it's disabled. Retryable: no. Fix: call `tools/list`
and use one of the names it returns. See [MCP tools](/mcp-tools).

### internal_error

Something failed on looot's side that isn't one of the named codes below. Retryable: yes. Fix:
retry once; if it fails again, report the `requestId`. See [Support](/support).

### not_found

Nothing matched the tool's arguments in this workspace (for example [`runs_get`](/mcp-tools/runs-get) with a `runId`
from another workspace, or one that never existed). Retryable: no. Fix: use an id this tool or
`runs_list` actually returned, then call again. See [`runs_list`](/mcp-tools/runs-list).

### endpoint_not_found

The `endpointId` argument doesn't match a catalog entry. Retryable: no. Fix: find a current id
with [`search_catalog`](/mcp-tools/search-catalog) or [`discover`](/mcp-tools/discover), then call again. See [`search`](/mcp-tools/search).

### workflow_not_found

The workflow id in the arguments doesn't exist in this workspace. Retryable: no. Fix: check the
workflow id and call again. See [`run`](/mcp-tools/run).

### idempotency_conflict

The `idempotencyKey` on a `run` call was already used with a different body. Retryable: no. Fix:
reuse the exact same body to replay the original run, or pick a new `idempotencyKey` for a new
one. See [Idempotency](/concepts/idempotency).

### forbidden

The signed-in token lacks the scope this tool needs (usually `runs.execute` for `run`). Retryable:
no. Fix: sign in again and keep the needed scope checked on the consent page, or use a token that
has it. See [Access](/concepts/access).

### insufficient_balance

`run` when the reservation would exceed the workspace's available balance. The data carries a
`topUp` object with a checkout link, the same as the REST 402; see
[REST errors: insufficient_balance](/errors/rest-errors#insufficient_balance). Retryable: no. See [Top up](/money/top-up).

### too_many_inflight_runs

`run` when this workspace already has too many runs in flight. Retryable: yes. Fix: wait for some
to finish, then call `run` again with the same idempotency key. See [Idempotency](/concepts/idempotency).

### rows_mode_unsupported

The request needs a storage capability this gateway isn't running in (for example
`output.mode: "canonical"` in production). Retryable: no. Fix: drop the option, or use
`output.mode: "raw"`. See [`inspect`](/mcp-tools/inspect).

### catalog_loading

The catalog hasn't finished loading in this gateway process yet. Retryable: yes. Fix: wait a few
seconds and call again.

### storage_busy

The storage backend is briefly overloaded. Retryable: yes. Fix: wait a few seconds and call again
with the same idempotency key. See [Idempotency](/concepts/idempotency).

### runs_cursor_invalid

The `cursor` argument to `runs_list` is invalid or stale. Retryable: no. Fix: call `runs_list`
again with no cursor. See [`runs_list`](/mcp-tools/runs-list).

### top_up_amount_out_of_range

[`top_up`](/mcp-tools/top-up)'s `amountUsd` is below the minimum (or above the maximum). Retryable: no. Fix: call
`top_up` again with at least `minimumUsd` from the error, or omit `amountUsd` entirely and let
looot pick a suggested amount. See [Top up](/money/top-up).

### top_up_unavailable

No payment link could be created for this workspace right now (reasons include billing being
disabled, a read-only token, or too many attempts in the last hour). Retryable: sometimes; read
the message. Fix: give whoever holds the account the `dashboardUrl` from the error, so they can
top up from `looot.ai/usage`. See [Top up](/money/top-up).

### unknown_category

`catalog_overview {category: "..."}` named a category id that doesn't exist. The data lists the
valid `categories`. Retryable: no. See [`catalog_overview`](/mcp-tools/catalog-overview).

### unknown_platform

`catalog_overview {platform: "..."}` named a platform id that doesn't exist, or one outside the
given `category`. Retryable: no. See [`catalog_overview`](/mcp-tools/catalog-overview).

## Service unavailable codes

A handful of internal service names can appear as `"<service>_unavailable"` (for example a
control-plane dependency being briefly down). These say which service and that a retry is safe.
Retryable: yes. Fix: wait a few seconds and call again; nothing was charged.

<Related />
