skip to content

In MCP, what is the -32022 UnsupportedProtocolVersionError and how should a client react?

level: middleimportance: must knowfreq 62%

answer

  1. it rejects a request, not a connection
  2. the error names its own remedy
  3. data lists what the server will serve
  4. retry with a listed revision
  5. reserved MCP error block, not standard JSON-RPC

basics

~20 s

UnsupportedProtocolVersionError, JSON-RPC code -32022, is how an MCP server rejects one request whose declared protocol version it will not serve. Its data carries supported and requested, so the client retries with a listed version rather than disconnecting.

solid answer

~50 s

In MCP revision 2026-07-28 every request declares its own protocol version, so version rejection is a **per-request** error rather than a failed handshake. A server that will not serve the declared revision answers that request with JSON-RPC error code `-32022`, `UnsupportedProtocolVersionError`. The `data` object carries `supported` — the list of revisions the server will serve — and `requested`, echoing what the caller declared. Because there is no session and nothing was negotiated, nothing has been invalidated: the right client behaviour is to intersect its own supported revisions with `data.supported`, pick one, and re-issue the same request declaring that version. Tearing down the transport is wrong and wasteful. If the intersection is empty, the client has genuinely incompatible peers and should surface that to the user rather than retry blindly. `-32022` sits in the `-32020`–`-32099` block that the MCP specification reserves for its own errors.

code

json · 12 lines
json
{
  "jsonrpc": "2.0",
  "id": 7,
  "error": {
    "code": -32022,
    "message": "Unsupported protocol version",
    "data": {
      "supported": ["2025-11-25"],
      "requested": "2026-07-28"
    }
  }
}

go deeper

for a junior

Know the name and the number: -32022 is UnsupportedProtocolVersionError, and the server includes the versions it does support so you can try again. Do not say the connection has to be closed.

for a middle

Explain the recovery algorithm end to end — read data.supported, intersect with your own revisions, re-issue the same request with a supported version, cache the result — and say why a per-request error cannot invalidate a connection.

for a senior

Talk about it operationally: a partly rolled fleet emits -32022 from some nodes and not others, logging requested alongside supported is what makes that diagnosable, and the fix is a uniform rollout rather than client pinning.

for a principal

Own the policy question of how many revisions your server keeps in its supported set, what that costs to test, and how you shrink it without stranding clients — including how discovery advertises the set so callers rarely have to hit the error at all.

## The error `UnsupportedProtocolVersionError` is the MCP-specific JSON-RPC error with code `-32022`. A server returns it when a request declares a protocol version the server is not willing to serve. The code lives in the `-32020`–`-32099` range, which the MCP specification reserves for itself on top of the standard JSON-RPC codes — so `-32022` is unambiguous protocol vocabulary, not something an implementation invented. ## Why it is per request, not per connection This is the framing that a candidate has to get right. Before revision 2026-07-28, versions were agreed once during the `initialize` handshake: a mismatch there meant the connection could not be established, and the client's only move was to give up or reconnect. Revision 2026-07-28 removed that handshake and made MCP a stateless protocol in which every request is self-contained and carries its own protocol version and capabilities. Servers must not rely on prior requests over the same connection to establish context, and an open connection — a stdio subprocess, say — is explicitly not a conversation or a session. The consequence for `-32022` is direct: it rejects **one request**. No agreement has been broken, because there was no agreement. The transport remains perfectly usable, and the very next request over the same connection may declare a different version and succeed. ## The data payload The error's `data` object carries two fields: - `supported` — the revisions the server is willing to serve, as an array of `YYYY-MM-DD` strings. - `requested` — the version the rejected request declared. `supported` is what makes recovery mechanical. The client does not have to guess or walk backwards one revision at a time; the server has told it the whole acceptable set in the rejection itself. `requested` is an echo that makes logs and traces self-describing when several concurrent requests are in flight, which matters precisely because the protocol is stateless and there is no per-connection version to attribute the failure to. ## The correct client algorithm 1. Catch `-32022` from the response to a specific request id. 2. Read `data.supported` and intersect it with the revisions the client itself implements. 3. If the intersection is non-empty, pick one — normally the newest — and **re-issue the same request** declaring that version. 4. Cache the choice for that server so subsequent requests do not repeat the round trip. 5. If the intersection is empty, stop. This is a genuine incompatibility; report it to the user with both lists rather than retrying in a loop. What you must **not** do is close the transport, kill the subprocess, or treat the server as unreachable. That reflex is a leftover from the handshake era, where a version mismatch really did doom the connection, and it is the single most common wrong answer to this question. ## Avoiding the round trip entirely A client that would rather not pay a failed request can learn the server's acceptable revisions before it calls anything, because a modern server must expose a discovery call that reports the revisions it supports. That turns `-32022` into a backstop for the cases discovery cannot cover — a server whose supported set changed after the client cached it, a client that skipped discovery for latency reasons, or a proxy in front of a fleet where different nodes were rolled at different times. ## Related errors, so you do not confuse them - `-32021`, `MissingRequiredClientCapabilityError`, with `data.requiredCapabilities`, is about capabilities, not versions: the server can serve your revision, but the operation needs something you did not declare. - `-32020`, `HeaderMismatchError`, is a Streamable HTTP transport error raised when the `MCP-Protocol-Version` header disagrees with the version in the request body. That is an internally inconsistent request, not an unsupported version, and it comes back with HTTP 400. - `-32602` is plain invalid params, used when a required metadata field is absent altogether rather than present and unacceptable. An interviewer will sometimes hand you a symptom and expect you to pick between these: "the server rejects everything with 400 and -32020" is a header/body wiring bug in your client, while "the server rejects with -32022 and lists only 2025-11-25" means you are talking to an older server and should either downgrade the declared version or accept that this server is legacy. ## Operational notes Behind a load balancer, a partially rolled fleet can produce `-32022` from some nodes and success from others for identical requests. Because MCP is stateless, any node can serve any request, so the fix is to make the rollout uniform on the supported-version set rather than to pin a client to a node. Log `requested` and `supported` together on every occurrence — that pair is usually enough to identify which half of a rollout you are hitting.

  • Why is disconnecting the wrong response to -32022 in revision 2026-07-28?
    Because nothing per-connection was established that a disconnect would reset. MCP has been stateless since 2026-07-28: each request is self-contained, and a version rejection scopes to that one request. The transport is still valid, so re-issuing the same request with a version from `data.supported` recovers immediately, while reconnecting throws away a working channel and changes nothing.
  • How does -32022 differ from -32021, MissingRequiredClientCapabilityError?
    `-32022` says the server will not serve the protocol revision you declared, and `data.supported` lists the ones it will. `-32021` says the revision is fine but the operation needs a client capability you did not declare, with `data.requiredCapabilities` naming which. One is fixed by changing the declared version, the other by declaring the capability — or by not calling that operation.
  • What should a client do when data.supported and its own revisions do not intersect at all?
    Stop and report. There is no version both sides can speak, so retrying is a loop that cannot converge. Surface both lists to the user or operator — requested, and what the server offers — because the resolution is a deployment change: upgrade the server, or run a client build that still speaks an older revision.

saying these in an interview costs you the question

  • Closes the transport or restarts the server process on a version rejection
  • Thinks -32022 means the whole connection is now unusable
  • Retries the identical request unchanged and hopes it succeeds
  • Cannot say what data.supported is for, or ignores it and guesses a version
  • Confuses it with -32020 HeaderMismatchError, which is a header-versus-body disagreement

context