Back to Blog
DevelopmentAug 6, 202612 min read

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

NT

Nikhil Tiwari

MCP Playground

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)
Python0 (generic)
C#-32002 (custom)
Rust-32002
Java-32002
Go-32002
PHP-32002
Kotlin-32603 (INTERNAL_ERROR)
Rubyleft to the implementor
Swiftno 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 →

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, or read what changed across the whole 2026 roadmap. Building fresh? Use the 2026 spec server guide.

MCP Error Code Reference

Code Name Meaning in MCP
-32700Parse errorMalformed JSON. Usually a serialization bug.
-32600Invalid requestValid JSON, invalid JSON-RPC envelope.
-32601Method not foundMethod doesn't exist. Check for version drift.
-32602Invalid paramsBad arguments — or a resource that doesn't exist.
-32603Internal errorServer-side crash. Kotlin also sent this for not-found.
-32000-32019Implementation-definedYours to use. -32000 is commonly "connection closed."
-32020-32099Reserved for MCPDo not use for custom errors.

For connection-level failures — -32000, timeouts, spawn errors, 406s — I wrote a separate troubleshooting guide. Auth failures have their own guide.

New to the protocol? Start with what the Model Context Protocol actually is, or see how MCP differs from a REST API.

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 → Scan your MCP server →

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 → — paste a URL, call a missing resource, and see exactly which code comes back.

NT

Written by Nikhil Tiwari

15+ years in product development. AI enthusiast building developer tools that make complex technologies accessible to everyone.

Build, compare & ship MCP agents — free

Connect any MCP server, compare 40+ models side-by-side, deploy hosted servers, and save reusable agents you can export as an API — all in your browser.

✦ Free credits on sign-up · no credit card needed

Try for Free →
MCP Error -32602: Every Error Code Change in the 2026-07-28 Spec