skip to content

A legacy JSON-RPC 1.0 client calls a strict JSON-RPC 2.0 server and its notifications start getting replies — why, and what else differs?

level: seniorimportance: nice to knowfreq 6%

answer

  1. how 1.0 marked a notification
  2. a member 1.0 never sent
  3. both result and error, or one
  4. named params and error codes are new

basics

~20 s

JSON-RPC 1.0 marks a notification with id null and sends no jsonrpc member, so a strict 2.0 server sees a request to answer or reject. 2.0 also adds named params, result-or-error responses, defined error codes, batches and reserved rpc. names.

solid answer

~50 s

In JSON-RPC 1.0 a notification is a request whose `id` is `null`; in 2.0 a notification is a request with no `id` member at all, and `"id": null` is merely a discouraged request id. A 1.0 message also lacks the `jsonrpc` member, which in 2.0 MUST be exactly `"2.0"`, so a strict 2.0 server answers each 1.0 notification with `-32600 Invalid Request` and a null `id` — replies the 1.0 client never expected. The other differences: 1.0 `params` is always an Array, while 2.0 adds by-name Objects; 1.0 responses carry both `result` and `error` with the unused one `null`, while 2.0 forbids both; 1.0 leaves the error object's shape open, while 2.0 defines `code`, `message`, `data` and reserved codes; 2.0 adds batches and reserved `rpc.` names; and 1.0's peer-to-peer model and `__jsonclass__` class hinting are not part of 2.0.

go deeper

for a junior

Recall that 2.0 messages carry jsonrpc "2.0" and 1.0 messages do not, and that the two versions mark notifications differently.

for a middle

Explain the main differences — params by name, result-or-error responses, defined error codes, batches — and why a null id means different things in each version.

for a senior

Diagnose unexpected replies or misread responses between a 1.0 peer and a 2.0 peer from a captured exchange, and say which side's rule produced each one.

for a principal

Plan the retirement of 1.0 traffic across an estate: where to tolerate it, how to detect it, and when the cost of dual handling outweighs the cost of forcing upgrades.

## Telling the versions apart JSON-RPC 1.0 (2005) and JSON-RPC 2.0 (2010, updated 2013) share a name and a purpose but not a wire format. The 2.0 specification is blunt about it: 2.0 Request and Response objects "may not work with existing JSON-RPC 1.0 clients or servers". It also gives the way to tell them apart — **a 2.0 message always has a `jsonrpc` member with the String value `"2.0"`, and a 1.0 message never does** — and suggests that most 2.0 implementations should consider trying to handle 1.0 objects. ## Side by side | Aspect | JSON-RPC 1.0 | JSON-RPC 2.0 | |---|---|---| | Version marker | none | `jsonrpc` MUST be exactly `"2.0"` | | `params` | an Array of arguments | optional; an Array by position or an Object by name | | `id` type | any type | String, Number or Null (Null discouraged, no fractions) | | Notification | a request whose `id` is null | a request with no `id` member | | Response | `result`, `error` and `id`, with the unused one of the first two null | `jsonrpc`, `id`, and exactly one of `result` or `error` | | Error object | "an Error object", shape not defined | `code` (integer), `message`, optional `data`; reserved codes | | Batch | not defined | an Array of Requests, with its own reply rules | | Reserved names | none | methods beginning `rpc.` | | Peer model | two peers, either may invoke methods | Client and Server roles; dual roles not addressed | | Class hinting | `__jsonclass__` to construct typed objects | not part of 2.0 | ## The notification trap, step by step Suppose a 1.0 client sends a notification to a strict 2.0 server: ```json {"method": "chat.typing", "params": ["ana"], "id": null} ``` 1. The text parses, so this is not `-32700`. 2. There is no `jsonrpc` member, and 2.0 says it MUST be exactly `"2.0"`, so this is not a valid 2.0 Request: the server answers `-32600 Invalid Request`. 3. The error Response carries `"id": null` — the value the specification requires when the id could not be determined, and the very value the message carried. 4. The 1.0 client, which expects silence for notifications, now receives a Response with a null `id` that it has no pending request for. A server that accepted the missing `jsonrpc` member but applied 2.0 rules would also reply — with a `result` and `id` null — because in 2.0 a null `id` makes a call, not a notification. The 2.0 specification names this exact confusion when it explains why a null `id` is discouraged: 1.0 used it for notifications. ## Responses: both members versus exactly one A 1.0 response always has three members: ```json {"result": "Hello JSON-RPC", "error": null, "id": 1} ``` A 2.0 response has exactly one of `result` and `error`, and the other MUST NOT exist. A 1.0 client written to the letter may test whether `error` is null; against a 2.0 server the member is simply absent, and how the client's code treats that absence decides whether it reports success correctly. In the other direction, a 2.0 client receiving a 1.0 response sees both members present, which 2.0 forbids. ## What 2.0 added and dropped Added in 2.0: - **Named parameters** through an Object, matched by exact name. - **A defined error object** with an integer `code`, a `message` and optional `data`, and the reserved codes `-32700`, `-32600` to `-32603` and the `-32000` to `-32099` server range. - **Batches**, with explicit rules for empty Arrays and all-notification batches. - **The `rpc.` prefix**, reserved for system extensions. - **Case-sensitive member matching**, stated as a convention. Not carried into 2.0: - **The peer-to-peer model** in which either side of a connection invokes methods on the other. 2.0 notes that one implementation could fill both roles but does not address it. - **Class hinting** with `__jsonclass__`. - **The transport sections.** 1.0 described streams, where non-valid requests or responses must close the connection, and HTTP with POST bodies; 2.0 is transport agnostic and answers invalid input with error Responses instead. ## Running a mixed estate - **Detect by `jsonrpc`.** Its presence is the specification's own test for 2.0. - **Apply each version's notification rule to its own traffic.** A null `id` means "do not reply" only for a 1.0 message. - **Translate error shapes deliberately.** A 1.0 client has no reserved-code table, so it cannot read meaning into `-32601` unless someone tells it. - **Retire 1.0 on a schedule.** The 2.0 text treats handling 1.0 objects as something implementations should consider, not a requirement, so tolerance on one server does not guarantee it on the next.

  • How can a JSON-RPC server that accepts both versions tell a 1.0 message from a 2.0 one?
    By the jsonrpc member. The 2.0 specification says 2.0 messages always carry jsonrpc with the String value "2.0" and 1.0 messages do not, and it suggests most 2.0 implementations should consider trying to handle 1.0 objects. A dual server can branch on that member and apply 1.0's rule — null id, no reply — only to 1.0 traffic.
  • What did JSON-RPC 1.0 say should happen to an invalid request on a stream connection, and what replaced it in 2.0?
    1.0's stream section said non-valid requests or responses must result in closing the connection, and that closing it must raise an exception for every unanswered request on each peer. 2.0 instead answers invalid input with an error Response: -32700 for text that is not valid JSON, -32600 for an invalid Request, with a null id when the id cannot be determined.

saying these in an interview costs you the question

  • JSON-RPC 1.0 and 2.0 both mark notifications with an id of null.
  • JSON-RPC 1.0 already supported named parameters through a params Object.
  • A 2.0 response, like a 1.0 one, carries both result and error.
  • JSON-RPC 1.0 defined the -32700 to -32603 codes; 2.0 only added batches.
  • The JSON-RPC version is announced in an HTTP header, not in the message.