MCP errors
DocSpace errors carry a stable, namespaced error_code next to an English message. Match on the
error_code; the message is for people and can change.
Errors come back in one of two shapes.
Transport errors reject the whole HTTP request before any tool runs. They are JSON-RPC errors
with the code in error.data.error_code:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Invalid or revoked agent key",
"data": { "error_code": "mcp.unauthorized" }
}
}
Tool errors come from a tool that ran (or was denied). The HTTP status is 200, the MCP result
has isError: true, and its text content is a JSON object:
{
"error": "Agent key is not allowed to call 'create_page' (requires scope 'pages:write' and a sufficient role)",
"error_code": "mcp.scope_denied"
}
mcp.* codes
error_code |
Shape | HTTP | JSON-RPC | Meaning | What to do |
|---|---|---|---|---|---|
mcp.unauthorized |
transport | 401 (WWW-Authenticate: Bearer) |
-32001 | No Authorization header, a header that is not Bearer dsk_<id>_<secret>, an unknown or wrong key, or a revoked key. Applies to every request, initialize included. |
Check the header; create a new key if this one was revoked or lost. |
mcp.scope_denied |
tool | 200 | (tool result) | The key lacks the tool's scope, or its role is below the scope's minimum role. | Do not retry. Tell the user which scope or role is missing (Scopes and roles). |
mcp.rate_limited |
transport | 429 (Retry-After: 60) |
-32000 | The key made more than 120 tool calls in 60 s. None of the calls in the request ran. | Wait 60 s, then retry with fewer calls. |
mcp.method_not_allowed |
transport | 405 (Allow: POST) |
-32000 | GET or DELETE on /mcp. The server is stateless: no SSE stream, no session. |
Use POST. |
mcp.forbidden_host |
transport | 403 | -32000 | Self-hosted local endpoint only: the request did not come from a loopback address with a localhost Host (and Origin, if sent). |
Connect from the same machine via localhost. |
mcp.service_role_missing |
transport | 503 | -32603 | Self-hosted only: the server has no SUPABASE_SERVICE_ROLE_KEY, so it cannot check keys or run tools. |
Configure the server (Self-hosting). |
mcp.internal_error |
transport | 500 | -32603 | Unexpected server failure (for example the key lookup failed). | Retry later; report it if it persists. |
mcp.invalid_request |
audit only | n/a | n/a | Recorded in the key's activity when the request was malformed (bad JSON-RPC shape, arguments, tool name or headers) and was rejected before dispatch. The client receives the standard JSON-RPC/MCP validation error. | Fix the request. |
mcp.dispatch_error |
audit only | n/a | n/a | Recorded when the MCP SDK rejected the tool name or arguments before the tool ran. The client receives the SDK's error. | Check the tool name and arguments against the tool reference. |
mcp.client_closed |
audit only | n/a | n/a | Recorded when the client disconnected before the response was sent. Calls that had not started never run. | None; retry if the call is still needed. |
The three audit only codes never appear in a response body: you see them in Settings → Knowledge → Agents and keys → Activity.
Tool error codes from other domains
Tools also return the codes of the domain they work on, in the tool-error shape:
error_code |
Returned by | Meaning |
|---|---|---|
page.not_found |
page tools | The page does not exist, is in another space, or is restricted to a team you cannot see. These three cases are indistinguishable on purpose. |
page.forbidden |
append_block |
The page's access list gives the key's creator less than Editor. |
context.invalid_input |
get_context |
Not exactly one of query, page_id, work_item_id, or a value out of range. |
context.not_found |
get_context |
The anchor page or work item is missing or not readable. |
context.forbidden |
get_context |
A work_item_id anchor without planner:read. |
context.failed |
get_context |
The context pack could not be built. |
mentions.invalid_term |
get_mentions |
The term is not 2–64 characters. |
mentions.invalid_range |
get_mentions |
from/to is not a valid ISO date range of at most 60 months. |
mentions.unavailable_encrypted |
get_mentions |
The space uses the strict protection level. |
mentions.failed |
get_mentions |
The count could not be computed. |
artifact.invalid_upload |
publish_artifact, update_artifact |
Empty or malformed HTML. |
artifact.unsupported_type |
publish_artifact, update_artifact |
The content is not HTML. |
artifact.too_large |
publish_artifact, update_artifact |
The HTML is over 10 MB. |
artifact.secrets_detected |
publish_artifact, update_artifact |
The HTML contains something that looks like a secret. Always refused over MCP. |
artifact.rate_limited |
publish_artifact, update_artifact |
More than 10 uploads per minute for this user. |
artifact.forbidden |
publish_artifact, update_artifact |
Editor role required. |
artifact.invalid_parent |
publish_artifact |
parent_page_id is missing, hidden or in another space. |
artifact.not_found |
update_artifact |
The page is missing, in another space, or not an artifact. |
artifact.storage_unavailable |
publish_artifact, update_artifact |
Artifact storage is not configured on the server. |
artifact.publish_failed |
publish_artifact, update_artifact |
Unexpected failure while publishing. |
auth.invalid_token |
tools with _token (self-hosted stdio only) |
The legacy user token could not be verified. |
auth.clerk_not_configured |
tools with _token (self-hosted stdio only) |
The server cannot verify user tokens. |
Errors without an error_code
Some older board, catalog, sync and planner tools still answer with a plain
{ "error": "<message>" } body and no error_code (for example not found, a membership
Forbidden: …, or the write limit Rate limited: …). Treat any body with an error field as a
failed call. These are being migrated to namespaced codes; this page will list the codes as they
land.
Related
- Scopes and roles: rate limits and the scope ∧ role check.
- Tool reference: parameters of every tool.