Click any words you think contain an error. Click again to unmark.
The response carries id 2 but the request was id 1. JSON-RPC correlates by echoing the request id, so the client can never match this reply.
Picture a busy coffee counter where everyone orders at once. The barista hands each order a numbered ticket and calls that number when the drink is ready. You hold ticket 1, but the barista shouts 'order 2!' and sets down your latte. You have no way to know it is yours, so you keep waiting while the coffee goes cold on the counter. JSON-RPC works the same way. The client tags each request with an id and keeps a list of tickets it is still waiting on. The server must reply with the exact same id so the client can match the answer to the right question. Here the request used id 1 and the reply came back as id 2, so the client never finds a match and the call stalls.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
This is a transport bug, and the whole trick is knowing to look at the transport first. Both frames are valid JSON. The tool ran. The result, Alice's row, is exactly what you would expect from SELECT * FROM users. Everything at the application layer looks healthy. Yet the exchange is broken.
The defect is one character of meaning: the request was sent with id: 1, and the response came back with id: 2. In JSON-RPC 2.0, that id is the sole mechanism a client uses to match a response to the request that produced it. When the ids disagree, the match fails, and the call quietly dies.
This deep dive explains what the id is for, how a client uses it, exactly what goes wrong here, the two server-side bugs that cause it, and why this class of defect only shows up when you trace the raw protocol frames rather than the application logs above them.
What the id field is actually for
JSON-RPC 2.0 is an asynchronous, multiplexed protocol. A single connection between an MCP host and an MCP server can carry many requests at the same time. The host does not have to send a tools/call, wait for the reply, then send the next one. It fires several off and processes responses as they arrive.
That design creates a matching problem. If three requests are in flight and a response shows up, which request does it answer? The protocol solves this with the id field. Every request that expects a reply carries a unique id. The server is required to copy that exact id into its response. The id is the join key between a request and its response.
There is a deliberate consequence here. Because matching is by id, responses are allowed to come back in any order. The server can finish request three before request one. Ordering on the wire means nothing; only the id matters. That is precisely why the id cannot be a fresh value, it has to be the request's own id echoed back.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
| Aspect | Correct response | Buggy response shown |
|---|---|---|
| id value | Echoes request id (1) | New value (2) |
| Client lookup | Finds pending entry for id 1 | Finds nothing for id 2 |
| Outcome | Call resolves with the result | Original call hangs and times out |
| Data payload | Alice's row, correct | Alice's row, still correct |
Real products, models, and research that use this idea.
- The mcp-inspector tool surfaces this by printing paired request and response frames so a mismatched id is visible at a glance.
- Claude Code and Cursor multiplex many concurrent tools/call requests over one stdio connection, so id correlation is what keeps replies from crossing wires.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow would you design the client's pending-request map to detect a mismatched or duplicate id early?
Key the map by request id; on response, fail loudly if the id is absent or already resolved, rather than silently dropping. Add a per-id timeout to convert hangs into clear errors.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Looking at the SQL or the result payload for the bug. The transport layer is broken first: the response id does not match the request id, so correlation fails.
60 second bullets to scan on the way to the call.
Why JSON-RPC needs an id on every request
How a client maps a response back to its request
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.