Skip to content

Errors

Section 13 · From docs/MCP.md on GitHub

13.1 Protocol errors versus tool errors

MCP has two channels for failure. A JSON-RPC error means the request itself was wrong; an isError: true tool result means the tool ran and failed, which the model can read and react to (fix its arguments, pick another asset). cloudg uses them like this:

Failure Tool call Resource read Prompt get Completion
Unknown or hidden name / URI JSON-RPC -32602 JSON-RPC -32002 (handshake) or -32602 (2026) JSON-RPC -32602 empty values
Invalid arguments isError, -32602 n/a JSON-RPC -32602 (missing required) n/a
Secret in an argument (standard and stricter) isError, -32602 JSON-RPC -32602 JSON-RPC -32602 JSON-RPC -32602
Policy denial isError, -31001 JSON-RPC -31001 JSON-RPC -31001 JSON-RPC -31001
Rate limit (policy, concurrency, live guard) isError, -31029 JSON-RPC -31029 JSON-RPC -31029 JSON-RPC -31029
Not found inside the handler (asset, dataset) isError, -32602 JSON-RPC -32002 / -32602 JSON-RPC error empty values
Path outside allowed roots isError, -31001 n/a n/a n/a
Timeout isError, "timeout" n/a n/a n/a
Unexpected exception isError, "handler_error" JSON-RPC -32603 "Internal error" (native) JSON-RPC -32603 empty values

(In-process, the "JSON-RPC" cells are raised MCPLayerError subclasses with the same code.)

13.2 Codes

Code Name Raised for
-32700 Parse error Body or line is not JSON
-32600 Invalid request Bad JSON-RPC envelope, batches where not allowed, not initialized, HTTP-level rejections in the native server's body
-32601 Method not found Unknown method, or a method of the other era
-32602 Invalid params InvalidArgumentsError, NotFoundError (unknown tool, prompt, resource on 2026, stale cursor), bad cursors
-32603 Internal error Base MCPLayerError, unexpected server errors; the live tools' PreflightError
-32002 Resource not found Unknown resource on handshake-era connections
-32020 Header mismatch 2026 HTTP headers that are missing or disagree with the body
-32022 Unsupported protocol version 2026 envelope with an unknown version; initialize on a 2026 connection
-31001 Access denied AccessDeniedError: policy denial, path outside the allowed roots, force=true on a live tool without the admin or operator role
-31029 Rate limited RateLimitedError: policy token bucket, concurrency queue timeout, the live operation guard (LiveOperationRefused, section 15)

cloudg's own codes sit in -31xxx because -32768 to -32000 belong to JSON-RPC and -32020 to -32099 are reserved by MCP.

13.3 What an error result looks like

cloudg/error_code is either one of the numeric codes above or a string (timeout, handler_error). cloudg/error_data carries the error's data, passed through the output pipeline. Real results:

Invalid arguments (cloudg mcp call find_assets --args '{"limit": 0, "colour": "red"}' --raw):

{
  "content": [{"type": "text", "text": "Invalid arguments: limit: Input should be greater than or equal to 1; colour: Extra inputs are not permitted"}],
  "isError": true,
  "_meta": {
    "cloudg/error_code": -32602,
    "cloudg/error_data": [
      {"loc": "limit", "msg": "Input should be greater than or equal to 1", "type": "greater_than_equal"},
      {"loc": "colour", "msg": "Extra inputs are not permitted", "type": "extra_forbidden"}
    ]
  }
}

Policy denial, from a policy with hide_denied: false and deny_categories: [admin]:

{
  "content": [{"type": "text", "text": "Access to tool 'reset' denied: category 'admin' denied by policy 'doc-demo'"}],
  "isError": true,
  "_meta": {"cloudg/error_code": -31001,
            "cloudg/error_data": {"policy": "doc-demo", "reason": "category 'admin' denied by policy 'doc-demo'"}}
}

Rate limit (one call per minute, second call):

{
  "content": [{"type": "text", "text": "Rate limit exceeded for tool 'boom' (1/minute); retry in 60.0s"}],
  "isError": true,
  "_meta": {"cloudg/error_code": -31029,
            "cloudg/error_data": {"retry_after": 60.0, "limit": "1/minute", "scope": "principal"}}
}

Timeout (a sync tool with timeout_seconds=0.5 asked to sleep 2 seconds):

{"content": [{"type": "text", "text": "Tool slow_sync timed out"}], "isError": true,
 "_meta": {"cloudg/error_code": "timeout"}}

Unexpected exception:

{"content": [{"type": "text", "text": "RuntimeError: could not reach arn:aws:iam::123456789012:role/Deploy"}],
 "isError": true, "_meta": {"cloudg/error_code": "handler_error"}}

That last one ran under a policy without transforms, so the ARN in the exception text came through. Under standard and stricter profiles the error text goes through the same output pipeline as data. The traceback is logged on the server (Tool boom failed, logger cloudg.mcp).

Protocol errors from the native server, in-process (session.handle(...), handshake era):

tools/call nope               {"code": -32602, "message": "Unknown tool: nope", "data": {"name": "nope"}}
resources/read test://items/omega
                              {"code": -32002, "message": "No item omega", "data": {"uri": "test://items/omega"}}
resources/read test://nothing {"code": -32002, "message": "Unknown resource: test://nothing", "data": {"uri": "test://nothing"}}
prompts/get greet (denied)    {"code": -31001, "message": "Access to prompt 'greet' denied: prompt denied by policy 'deny-prompts'", "data": {"policy": "deny-prompts", "reason": "prompt denied by policy 'deny-prompts'"}}
tools/call with "arguments": "oops"
                              {"code": -32602, "message": "arguments must be an object"}
no/such                       {"code": -32601, "message": "Method not found", "data": "no/such"}
resources/read test://items/omega on a 2026-07-28 request
                              {"code": -32602, "message": "No item omega", "data": {"uri": "test://items/omega"}}

In-process, the same failures raise. From the catalog:

read_resource("cloudg://nope")                     NotFoundError -32602 "Unknown resource: cloudg://nope"
read_resource("cloudg://assets/does-not-exist")    ReferenceNotFoundError -32602 "No asset matches the reference in this dataset. ..." data={"value": "does-not-exist", "dataset": "prod", "suggestions": []}
get_prompt("investigate_asset", {})               InvalidArgumentsError -32602 "Missing required prompt arguments: ref"

The exception classes, all in cloudg.mcp.core and re-exported from cloudg.mcp:

class MCPLayerError(Exception):          # code -32603; .message, .data
class InvalidArgumentsError(MCPLayerError):   # -32602
class NotFoundError(MCPLayerError):           # -32602
class AccessDeniedError(MCPLayerError):       # -31001
class RateLimitedError(MCPLayerError):        # -31029

cloudg.mcp.state adds NoDatasetError and ReferenceNotFoundError, both NotFoundError subclasses. Raise them from your own handlers with a helpful message and data=; the model sees both.