# Why Did My MCP Tool Call Fail? MCP Error Codes and Tool Errors Explained

> What each MCP error code means, from -32602 to -32000 and -32001, how protocol errors differ from isError tool results, and who hit them.

Source: https://flowlines.ai/blog/mcp-error-codes · Updated 2026-10-22 · By Flowlines
Index of all Flowlines pages for AI agents: https://flowlines.ai/llms.txt

An MCP tool call fails in one of three places: the connection or client SDK gives up (often shown as **-32000 Connection closed** or **-32001 Request timed out**, which are SDK codes, not specification codes), the server rejects the request with a JSON-RPC protocol error such as **-32602 Invalid params**, or the tool runs and returns a result with `isError: true`. **Flowlines shows which of your tools fail, with which kind of error, and which users and AI clients those failures affect.**

The codes below are checked against the [MCP specification](https://modelcontextprotocol.io/specification/2026-07-28/basic) for the 2026-07-28, 2025-11-25 and 2025-06-18 revisions, and against the official TypeScript and Python SDK source. Where the specification and the SDKs disagree, this guide says so.

## What is the difference between an MCP protocol error and a tool error?

A protocol error is a JSON-RPC error response that says the request could not be handled; a tool error is a normal result with `isError: true` that says the tool ran and failed. Flowlines counts both, separately, for every tool.

| Aspect | Protocol error | Tool error |
|---|---|---|
| Shape on the wire | An `error` object with `code` and `message` | A `result` with `isError: true` and text content |
| Meaning | The request could not be processed | The tool ran and reports a failure |
| Typical causes | Unknown tool, malformed request, unsupported method, server crash | Upstream API failed, record not found, invalid input values |
| Who sees it | The client, which may not show it to the model | The model, which can read it and try again |
| Owner | MCP server or client integration | The tool's own logic |

A protocol error for an unknown tool looks like this:

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": { "code": -32602, "message": "Unknown tool: search_custmers" }
}
```

A tool error is a successful JSON-RPC response whose result reports the failure:

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [{ "type": "text", "text": "start_date must be YYYY-MM-DD. Received: 22/10/2026." }],
    "isError": true
  }
}
```

The difference matters because the model only learns from what it sees. Since the 2025-11-25 revision, the specification says input validation errors, such as a date in the wrong format or a value out of range, should be returned as tool errors rather than protocol errors so the model can correct itself, and clients should pass tool errors to the model. The schema still describes `-32602` as covering invalid tool arguments, so servers in production do both. Count them as separate classes and do not assume one convention.

## What do MCP error codes mean?

MCP uses the five standard JSON-RPC codes, a few codes of its own, and several codes that only SDKs define. Flowlines groups failed calls by these classes so the right owner sees them.

| Code | Name | Defined by | Typical cause | First check |
|---|---|---|---|---|
| -32700 | Parse error | JSON-RPC | The message is not valid JSON | Stray output in the stream, especially on stdio |
| -32600 | Invalid request | JSON-RPC | Valid JSON, but not a valid request | Client and server protocol versions |
| -32601 | Method not found | JSON-RPC | Method not implemented, or a capability the server did not advertise | Capabilities and the protocol revision in use |
| -32602 | Invalid params | JSON-RPC | Unknown tool, invalid arguments, unknown resource (2026-07-28), invalid cursor | Tool name, input schema and arguments sent |
| -32603 | Internal error | JSON-RPC | Unhandled exception in the server | Server logs and the stack trace for that request |
| -32002 | Resource not found | MCP, until 2025-11-25 | Unknown resource URI | Retired in 2026-07-28, which uses -32602 |
| -32042 | URL elicitation required | MCP 2025-11-25 only | The user must complete a step in the browser | Retired in 2026-07-28 |
| -32020 | Header mismatch | MCP 2026-07-28 | HTTP headers disagree with the request body | Proxies or clients that rewrite headers |
| -32021 | Missing required client capability | MCP 2026-07-28 | The server needs a capability the client did not declare | Client capabilities sent with the request |
| -32022 | Unsupported protocol version | MCP 2026-07-28 | The server does not support the requested revision | Supported versions returned by the server |
| -32000 | Connection closed | TypeScript and Python SDKs | The transport closed before a response | Server process, command and stdout |
| -32001 | Request timed out | TypeScript SDK | No response within the client timeout | Tool duration and client timeout |

The 2026-07-28 revision calls the range -32000 to -32019 legacy and implementation-defined, and tells receivers not to assume a meaning for those codes. In practice, -32000 and -32001 still appear in error messages from popular clients built on the SDKs, which is why people search for them.

## What does MCP error -32000 "Connection closed" mean?

It means the connection to the server ended before the response arrived; the client SDK raises it locally, and the specification does not define it. For a local stdio server, the cause is almost always the server process.

- **The process crashed or exited at startup.** Run the exact command from the client configuration in a terminal and read what it prints.
- **The command or path is wrong.** Clients often start with a different `PATH` from your shell, so `npx`, `uvx` or `node` may not resolve. Use absolute paths.
- **Something writes to stdout.** On stdio, stdout carries only protocol messages. A `print` or a library banner corrupts the stream; log to stderr instead.
- **A required environment variable is missing,** so the server exits before it can respond.
- **For remote servers,** a proxy or load balancer closed the stream, or the server restarted mid-request.

One trap: the TypeScript SDK v1 HTTP server also returns -32000 over the network for several HTTP-level problems, such as a missing session header or a server that was not initialized. If a client on the stateless 2026-07-28 revision talks to an older server, the message text tells you which case you have.

## What does MCP error -32001 "Request timed out" mean?

It means the client stopped waiting; the TypeScript SDK raises -32001 locally after 60 seconds by default, and the specification assigns no code to timeouts. The tool may still be running on the server.

| Situation | What to do |
|---|---|
| The tool is genuinely slow | Measure p95 duration per tool, then speed up or split the work |
| The tool is slow but makes progress | Send progress notifications and let the client reset its timeout on progress |
| The work is long by design | Return early with a handle and let the agent check back, or use the tasks extension in 2026-07-28 |
| Only one client times out | That client may use a shorter timeout; compare durations per client |

The same number can mean something else: the TypeScript SDK v1 HTTP server sends -32001 with "Session not found" when a client presents a session that no longer exists, for example after a server restart. The Python SDK v1 reports timeouts with code 408 instead. Read the message, not just the number.

## What does MCP error -32602 "Invalid params" mean for a tool call?

For `tools/call` it most often means the tool name is unknown, and some servers also use it for arguments that do not match the input schema. In the 2026-07-28 revision it also covers a resource URI that does not exist and an invalid pagination cursor.

1. **Compare the tool name with `tools/list`.** Names are case-sensitive. A renamed or removed tool, or a client that cached an old list, produces this error.
2. **Validate the arguments against the input schema** you publish, including required fields and formats.
3. **Check which client sent it.** If one client produces most -32602 errors, it may send arguments your schema does not expect.
4. **Prefer tool errors for bad argument values.** A message such as "date must be YYYY-MM-DD" in an `isError` result lets the model fix the call.

## Why does my MCP tool return isError: true?

Because the tool ran and decided the call failed: an upstream API refused or timed out, a record was not found, or an argument value was invalid. These are the errors the model can act on, so the message text is part of your tool's design.

| Weak tool error | Better tool error |
|---|---|
| "Error 500" | "The billing API is unavailable. Try again in a minute or ask the user to retry later." |
| "Invalid input" | "start_date must be YYYY-MM-DD and before end_date." |
| "Not found" | "No customer matches acme.io. Search by company name with search_customers." |
| A stack trace | One sentence on what failed and what the agent can do next |

Track tool errors per tool and per error message. A tool whose errors are followed by the same call over and over is telling the model too little; [why agents ignore your MCP tools](https://flowlines.ai/blog/unused-mcp-tools) covers repeated calls in detail.

## Which MCP error codes changed in the 2026-07-28 specification?

The five standard JSON-RPC codes did not change; resource not found moved to -32602, the URL elicitation code was retired, and three new codes cover headers, capabilities and versions.

| Code | 2025-11-25 | 2026-07-28 |
|---|---|---|
| -32002 | Resource not found | Retired; servers use -32602, and clients should still accept -32002 from older servers |
| -32042 | URL elicitation required | Retired |
| -32020 | Not defined | Header mismatch |
| -32021 | Not defined | Missing required client capability |
| -32022 | Not defined | Unsupported protocol version |
| -32000 to -32019 | Not reserved | Legacy, implementation-defined |

If you saw -32001, -32003 or -32004 for these new conditions in a release candidate, those numbers were draft-only and were renumbered before the final release. For the other changes in the revision, read [MCP analytics after the stateless spec](https://flowlines.ai/blog/mcp-analytics-stateless-spec).

## How do I find which users hit MCP errors?

Record a few fields on every failed call and group failures by tool, then by user and AI client; Flowlines does this grouping for you and shows the sessions behind each failure.

| Field to record | Why it matters |
|---|---|
| Tool name | Ranks failures by tool, not by server |
| Error class: transport, protocol, tool error, timeout | Sends each failure to the right owner |
| JSON-RPC code and a short message | Separates unknown tools from internal errors |
| AI client name and version | Finds problems that only one client has |
| Permitted user or account identifier | Shows whether one customer or many are affected |
| Server version | Lines failures up with releases |

Then read the pattern. One user failing many times is usually account data or permissions; many users failing once each points to the tool. [MCP tool analytics](https://flowlines.ai/mcp-tool-analytics#users-hitting-errors) shows which users hit errors, and [MCP user analytics](https://flowlines.ai/mcp-user-analytics) shows each user's clients and sessions. Keep error ratios and p95 latency per tool in your metrics backend as well; the [MCP monitoring guide](https://flowlines.ai/blog/mcp-observability-guide) covers success rate and alert rules, and [alerts when MCP errors spike](https://flowlines.ai/questions/mcp-error-spike-alerts) has starting thresholds.

**Key takeaway:** separate transport, protocol and tool errors before you count them, read the message as well as the code, and connect each failure to the users and clients it affects. Flowlines shows failing tools with the users and sessions behind them, and the [Free plan](https://flowlines.ai/pricing) includes 100 analyzed MCP sessions per month.

## Sources

- [MCP specification 2026-07-28: base protocol and error codes](https://modelcontextprotocol.io/specification/2026-07-28/basic)
- [MCP specification 2026-07-28: tools and error handling](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)
- [MCP 2026-07-28 changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog)
- [MCP 2025-11-25 changelog](https://modelcontextprotocol.io/specification/2025-11-25/changelog)
- [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification)
- [MCP TypeScript SDK v1 error codes](https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/src/types.ts)
## Frequently asked questions

### What is the difference between an MCP protocol error and a tool error?

A protocol error is a JSON-RPC error response: the request could not be handled, for example an unknown tool or a malformed request. A tool error is a normal result with isError: true: the tool ran and reports that it failed. Clients should pass tool errors to the model so it can correct itself.

### What does MCP error -32000 Connection closed mean?

The connection to the server ended before a response arrived. The MCP specification does not define -32000; the TypeScript and Python SDKs use it locally. For stdio servers, the usual causes are a process that crashed or exited, a wrong command or path, or log output written to stdout.

### What does MCP error -32001 Request timed out mean?

The client SDK stopped waiting for a response. The TypeScript SDK raises -32001 locally after 60 seconds by default. Make the tool faster, send progress notifications and let the client reset its timeout on progress, or raise the timeout in the client. The specification itself assigns no code to timeouts.

### What does MCP error -32602 Invalid params mean?

The request parameters were rejected. For tools/call it usually means an unknown tool name, and some servers also use it for arguments that do not match the input schema. In the 2026-07-28 revision it is also the code for a resource that does not exist and for an invalid pagination cursor.

### Did MCP error codes change in the 2026-07-28 specification?

Yes. Resource not found moved from -32002 to -32602, the URL elicitation code -32042 was retired, and three codes were added: -32020 header mismatch, -32021 missing required client capability and -32022 unsupported protocol version. The five standard JSON-RPC codes did not change.

### How do I find which users hit MCP tool errors?

Record the tool name, error class, JSON-RPC code, AI client and a permitted user identifier on every failed call, then group failures by tool and user. Flowlines shows which tools fail, with which kind of error, and which users and AI clients the failures affect.
