# MCP Error -32602: Every Error Code Change in the 2026-07-28 Spec

> Resource not found moved from -32002 to -32602, and the whole JSON-RPC server-error range got repartitioned. If your server uses a custom code in the -32020 to -32099 band, you are now squatting on reserved space. Here is every change, and the fix for client and server authors.

**Source:** https://mcpplaygroundonline.com/blog/mcp-error-32602  
**Author:** Nikhil Tiwari  
**Published:** 2026-08-06  
**Updated:** 2026-08-06  
**Category:** Development  
**Reading time:** 12 min read

---

TL;DR

-   **Resource not found is now `-32602`**, not `-32002`. Landed in the 2026-07-28 spec via SEP-2164.
-   **The server-error range was repartitioned.** `-32000`–`-32019` stays implementation-defined; `-32020`–`-32099` is now reserved for the spec.
-   **Three codes were renumbered:** HeaderMismatch, MissingRequiredClientCapability, and UnsupportedProtocolVersion all moved.
-   **Clients should accept both `-32602` and `-32002`** during the transition — but gate on the method name.
-   **Never return an empty `contents` array** for a missing resource. The spec forbids it.

I lost most of a Tuesday to **MCP error -32602**.

My client worked against a TypeScript server. The same code, pointed at a Go server, silently stopped detecting missing resources.

Nothing crashed. No stack trace. **The error just stopped matching my handler.**

The cause turned out to be embarrassing. The two servers returned different error codes for an identical condition.

That was not a bug in either one. Until the 2026-07-28 spec, the SDKs genuinely disagreed.

That revision fixed it — and quietly repartitioned the entire JSON-RPC server-error range while it was in there. **Most migration guides skipped the second part.**

If your server defines a custom error code, there is a real chance it is now sitting in reserved space.

## What MCP Error -32602 Actually Means

`-32602` is not an MCP invention. **It comes from the JSON-RPC 2.0 specification, where it means "Invalid params."**

The method you called exists. The arguments you passed do not satisfy it.

In MCP that now covers three distinct situations.

-   **A tool call whose arguments fail the tool's JSON schema.** Wrong type, missing required field, bad enum value.
-   **Structured output that does not match the declared output schema.**
-   **A `resources/read` for a URI that does not exist.** This is the new one.

That third case is what trips people up. **A missing resource is now framed as an invalid parameter** — you passed a URI pointing at nothing.

It reads oddly at first. It is the correct JSON-RPC framing, and it avoids inventing a custom code.

**Retrying a -32602 will always fail.** The parameters are invalid and will still be invalid on attempt two. I have watched a client burn its entire backoff budget re-sending a request that was never going to succeed.

## Why -32002 and -32602 Both Mean "Resource Not Found"

Here is the history, because it explains every confusing thing about this error.

**The original spec recommended `-32002` for resource-not-found.** That looked reasonable. It was also wrong, for a specific reason.

JSON-RPC reserves the `-32000` to `-32099` band for _implementation-defined_ server errors. That range belongs to your application.

**It was never meant for protocol-level semantics.** A condition every MCP server can hit is protocol-level by definition.

SEP-2164 moved it to `-32602`, and the change shipped in the 2026-07-28 revision. The changelog entry is blunt: _"Change resource not found error code from -32002 to -32602 (Invalid Params) to align with JSON-RPC specification."_

### Four Codes, One Condition

This is the part that cost me a day. **The official SDKs never agreed with each other.**

For the exact same condition — reading a resource that does not exist — here is what each SDK sent before the standardization:

SDK

Error code returned

TypeScript

`-32602` (InvalidParams)

Python

`0` (generic)

C#

`-32002` (custom)

Rust

`-32002`

Java

`-32002`

Go

`-32002`

PHP

`-32002`

Kotlin

`-32603` (INTERNAL\_ERROR)

Ruby

left to the implementor

Swift

no built-in handler

**Four different codes across the eight SDKs that handled it at all.** Five sent `-32002`. One sent `-32602`. One sent `-32603`. One sent `0`.

That is why my client "broke" when I switched servers. It never broke. **It was written against one SDK's dialect.**

If you have ever thought _"my MCP client works with some servers and not others"_ — this table is very likely your answer.

Which dialect does your server speak?

Paste a URL, call a resource that doesn't exist, read the raw JSON-RPC error frame.

[Test any MCP server free →](/mcp-test-server)

## The Error Code Range Was Repartitioned Too

This is the change almost nobody covered, and it is the one most likely to bite a server author.

**The 2026-07-28 spec split the JSON-RPC server-error range in two.**

-   **`-32000` to `-32019`** — still implementation-defined. Existing SDK usage is explicitly grandfathered.
-   **`-32020` to `-32099`** — now reserved for the MCP specification.

If you invented a custom error code anywhere in that upper band, **you are now squatting on space the spec has claimed.** Nothing breaks today. It will collide eventually.

Three spec codes were renumbered to fit the new policy:

Error

Was

Now

HeaderMismatch

`-32001`

`-32020`

MissingRequiredClientCapability

`-32003`

`-32021`

UnsupportedProtocolVersion

`-32004`

`-32022`

These were introduced during the 2026-07-28 draft cycle, so **you only hit them if you built against a release candidate.** If you did, grep for the old literals.

## How to Fix -32602 as a Client Author

The migration guidance is explicit: **treat both `-32602` and `-32002` as resource-not-found during the transition.**

Do not pick one. Accept both.

```
const RESOURCE_NOT_FOUND = new Set([
  -32602, // canonical, per SEP-2164
  -32002, // legacy: C#, Rust, Java, Go, PHP
]);

function isResourceNotFound(err, method) {
  if (method !== 'resources/read') return false;
  return RESOURCE_NOT_FOUND.has(err.code);
}
```

**Notice the method guard. It matters more than the code check.**

`-32602` is heavily overloaded — it also means "your tool arguments failed validation."

Without gating on `resources/read`, **you will classify schema failures as missing resources.** That is a worse bug than the one you set out to fix.

For the two stragglers you need a fallback.

-   **Kotlin servers sent `-32603`**, which genuinely means internal error. Mapping that to not-found would swallow real crashes.
-   **Python servers sent `0`**, which means nothing at all.

For both, match on the `message` string as best-effort. **Log when you hit that path** so you can delete it once the SDKs catch up.

**The silent failure to watch for:** if your client hardcodes `if (err.code === -32002)`, that branch is now dead against any updated server. No exception, no warning — it just stops being taken.

## How to Fix -32602 as a Server Author

Your obligation is short. **Return `-32602` when the requested resource does not exist.** The spec language is MUST.

```
{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32602,
    "message": "Resource not found",
    "data": {
      "uri": "file:///nonexistent.txt"
    }
  }
}
```

**Put the missing URI in `data`.** The spec marks this SHOULD, and skipping it is a false economy.

A client that gets the URI back can log exactly what failed. One that does not gets "something wasn't found" and no way to act.

If you maintain a server on C#, Rust, Java, Go, or PHP, **you were emitting `-32002`** unless you overrode it. Check your SDK version — the fix may arrive on your next bump.

### The Empty Contents Array Trap

This one is subtle, and the spec calls it out directly. **Servers MUST NOT return an empty `contents` array for a resource that does not exist.**

It looks harmless. Return `{ "contents": [] }` and let the client work it out.

The problem is that **the response is ambiguous.** An empty array can mean two different things.

-   The resource exists and genuinely has no content.
-   The resource does not exist at all.

The client cannot tell them apart. **A "success" response for a missing resource is worse than an error** — it fails silently, and silent failures surface three layers downstream.

## What Else Changed in 2026-07-28

The error codes were a minor entry in a revision that rewrote the transport. If you are debugging after an upgrade, **you probably picked up several of these at once.**

-   **Sessions are gone.** The `Mcp-Session-Id` header was removed entirely.
-   **The handshake is gone.** No more `initialize` / `notifications/initialized`. Every request carries its protocol version and capabilities in `_meta`.
-   **`server/discover` is mandatory.** Servers MUST implement it to advertise supported versions, capabilities, and identity.
-   **`subscriptions/listen` replaces** the HTTP GET endpoint and `resources/subscribe`.
-   **`ping`, `logging/setLevel`, and `notifications/roots/list_changed` were removed.**
-   **All results now require a `resultType` field** — `"complete"` or `"input_required"`.
-   **MRTR replaces server-initiated requests** like `sampling/createMessage` and `elicitation/create`.
-   **SSE resumability is gone.** No `Last-Event-ID`; a broken stream means re-issuing the request with a new ID.
-   **Roots, Sampling, and Logging are deprecated**, as is HTTP+SSE and OAuth Dynamic Client Registration.

The stated goal is that _any request can now land on any server instance behind a plain round-robin load balancer._

**Debug these one at a time.** Bump your SDK, verify, then change transport behaviour. Doing both at once is how a one-hour fix becomes a lost Tuesday.

Not migrated yet? Start with the [2026-07-28 migration guide](/blog/migrate-mcp-server-2026-07-28-stateless), or read [what changed across the whole 2026 roadmap](/blog/mcp-2026-roadmap-whats-changing-for-developers). Building fresh? Use the [2026 spec server guide](/blog/build-mcp-server-2026-spec).

## MCP Error Code Reference

Code

Name

Meaning in MCP

`-32700`

Parse error

Malformed JSON. Usually a serialization bug.

`-32600`

Invalid request

Valid JSON, invalid JSON-RPC envelope.

`-32601`

Method not found

Method doesn't exist. Check for version drift.

**`-32602`**

**Invalid params**

**Bad arguments — or a resource that doesn't exist.**

`-32603`

Internal error

Server-side crash. Kotlin also sent this for not-found.

`-32000`–`-32019`

Implementation-defined

Yours to use. `-32000` is commonly "connection closed."

`-32020`–`-32099`

Reserved for MCP

Do not use for custom errors.

For connection-level failures — `-32000`, timeouts, spawn errors, 406s — I wrote a [separate troubleshooting guide](/blog/mcp-server-troubleshooting-common-errors-fix). Auth failures have [their own guide](/blog/mcp-server-oauth-authentication-guide).

New to the protocol? Start with [what the Model Context Protocol actually is](/blog/what-is-model-context-protocol), or see [how MCP differs from a REST API](/blog/mcp-vs-rest-api-whats-different).

## How MCP Playground Helps

Reproducing this locally means wiring a client, pointing it at a server, and reading logs.

**I built MCP Playground to skip that.** Paste a server URL in the browser, call `resources/read` against a URI you know doesn't exist, and read the raw JSON-RPC error frame — code, message, and `data` payload.

That tells you in seconds which dialect a server speaks, **before you write a line of client code against it.**

See the real error frame in your browser

No install. No signup for the basic test.

[Test any MCP server free →](/mcp-test-server) [Scan your MCP server →](/mcp-security-scanner)

## Frequently Asked Questions

**What does MCP error -32602 mean?+**

It is the JSON-RPC "invalid params" code. The method exists but your arguments are wrong. Since the 2026-07-28 spec it is also the canonical code for a resource that does not exist.

**Should I retry on -32602?+**

No. The parameters are invalid and will still be invalid on retry. Fix the arguments instead.

**Is -32002 deprecated?+**

As a recommendation, yes — the 2026-07-28 spec replaced it with -32602. In the wild it is still common, since five official SDKs emitted it.

**Should my client handle both -32002 and -32602?+**

Yes. Accept both during the transition, and gate on the method name so you do not misclassify tool schema errors as missing resources.

**Can I still use custom error codes?+**

Only in -32000 to -32019. The -32020 to -32099 band is now reserved for the MCP specification. Existing usage below -32020 is grandfathered.

**Can I return an empty contents array instead of an error?+**

No. The spec forbids it. An empty array cannot be distinguished from a resource that exists but has no content.

## Wrapping Up

**\-32602 is the canonical MCP code for resource-not-found**, and -32002 is the legacy code five official SDKs still emit. Accept both on the client, gated on the method name. Send -32602 with the missing URI in `data` on the server, and never an empty `contents` array.

Then check your custom error codes. **If any sit between -32020 and -32099, move them down.**

[Test any MCP server free →](/mcp-test-server) — paste a URL, call a missing resource, and see exactly which code comes back.

## Frequently asked questions

### What does MCP error -32602 mean?

It is the JSON-RPC "invalid params" code, meaning the method exists but the arguments are wrong. Since the 2026-07-28 specification it is also the canonical error code for a resource that does not exist, replacing -32002.

### Should I retry a request that failed with -32602?

No. The parameters are invalid and will still be invalid on a retry. Fix the arguments or the resource URI instead of retrying.

### Is the MCP error code -32002 deprecated?

Yes, as a recommendation. The 2026-07-28 specification changed resource not found from -32002 to -32602 to align with JSON-RPC. Five of the official SDKs emitted -32002, so it is still common in the wild.

### Should my MCP client handle both -32002 and -32602?

Yes. Accept both codes as resource-not-found during the transition period. Gate the check on the resources/read method name, because -32602 also signals tool argument schema failures and you would otherwise misclassify them.

### Can I still define custom MCP error codes?

Only in the -32000 to -32019 range. The 2026-07-28 specification reserved -32020 to -32099 for the MCP specification itself. Existing implementation-defined usage below -32020 is grandfathered.

### Can an MCP server return an empty contents array for a missing resource?

No. The specification forbids it. An empty array is ambiguous because it cannot be distinguished from a resource that exists but has no content. Return the -32602 error instead.


---

_Canonical page: https://mcpplaygroundonline.com/blog/mcp-error-32602 — MCP Playground (mcpplaygroundonline.com), the free browser-based tool for testing MCP servers and building AI agents._
