Skip to content

Faults

A fault is data, not a transport failure. It arrives as a normal 200 whose tool result carries isError: true; the server answered, and the answer was a refusal. Only a protocol-level problem uses the JSON-RPC error channel.

This matters for retries: a refusal re-earns itself, so retrying one is wasted work. Repair the call instead — field names what to fix, and details carries the allowed values when the server can enumerate them.

  • conflict
  • dependency_unavailable
  • forbidden
  • gone
  • internal
  • not_found
  • payload_too_large
  • rate_limited
  • unauthenticated
  • validation
{
"_meta": {
"io.modelcontextprotocol/serverInfo": {
"name": "vectadyne",
"version": "0.1.0-local"
}
},
"content": [
{
"text": "\u003cprose\u003e",
"type": "text"
}
],
"isError": true,
"resultType": "complete",
"structuredContent": {
"error": {
"code": "validation",
"field": "scope_key",
"message": "\u003cprose\u003e",
"request_id": "\u003cper-call\u003e",
"retryable": false
}
}
}

retryable is the server’s judgement, not a guess from the code. Honour it: retrying a validation fault only re-earns it, and retrying past a rate limit deepens it. request_id is what makes a bug report actionable.