Preserve the Request ID in Invalid Request Error Responses
## Motivation and Context Resolves #398. `JsonRpcHandler` returned `"id": null` for JSON-RPC envelope errors (wrong or missing `jsonrpc` version, non-string `method`) even when the incoming payload carried a concrete, valid request id. From the client's perspective the original request stayed pending forever, since no response with the matching id ever arrived: ``` {"jsonrpc":"1.0","id":3,"method":"ping","params":{}} => {"jsonrpc":"2.0","id":null,"error":{"code":-32600,...}} ``` Per JSON-RPC 2.0 (Response object, `id`), the response id MUST be the same as the request's id; null is reserved for requests whose id could not be detected (e.g. Parse error). The envelope validation in `process_request` runs after JSON parsing with the request hash in hand, so the id is detectable - it was simply discarded by passing the `:unknown_id` sentinel unconditionally. The fix passes the request's id to `error_response` whenever the request carries one. The existing guards keep every other case intact: `error_response` already nils out ids that fail validation (so a type-invalid or pattern-violating id still yields `"id": null`, preserving the XSS-prevention policy on echoed ids), and the `:unknown_id` sentinel is kept for requests without an id so an Invalid Request error still produces a null-id response, matching the spec's own example. Parse errors and non-object requests are unchanged (null is correct there), and batch entries pick up the fix automatically since each entry goes through `process_request`. For reference, the TypeScript and Python SDKs currently respond with `"id": null` in these cases as well, because both validate the whole message against a schema (zod / pydantic) before extracting the id. This change is driven by the JSON-RPC 2.0 requirement rather than parity; the Ruby transport layer already echoes the request id where recoverable (`invalid_request_response(request_id:)` in `StreamableHTTPTransport`), and this closes the remaining gap in `JsonRpcHandler`. ## How Has This Been Tested? - Reproduced the three payloads from #398 before and after the fix; they now return `"id":3`, `"id":4`, and `"id":8` respectively - New and updated unit tests covering: id preserved for wrong version, missing `jsonrpc`, numeric `method`, and `rpc.`-prefixed `method`; id preserved for an invalid entry inside a batch; null id kept for requests without an id, with an explicit null id, and with an id that fails validation ## Breaking Changes None in the protocol sense - this aligns the error responses with JSON-RPC 2.0. Clients that matched `"id": null` on these envelope errors will now see the request's own id, which is what allows them to correlate the error with the pending request.
This commit is contained in:
parent
9193745291
commit
18761cf367
1 changed files with 82 additions and 4 deletions
|
|
@ -73,13 +73,18 @@ module JsonRpcHandler
|
|||
|
||||
error = if !valid_version?(request[:jsonrpc])
|
||||
"JSON-RPC version must be 2.0"
|
||||
elsif !valid_id?(request[:id], id_validation_pattern)
|
||||
elsif !valid_id?(id, id_validation_pattern)
|
||||
"Request ID must match validation pattern, or be an integer or null"
|
||||
elsif !valid_method_name?(request[:method])
|
||||
'Method name must be a string and not start with "rpc."'
|
||||
end
|
||||
|
||||
return error_response(id: :unknown_id, id_validation_pattern: id_validation_pattern, error: {
|
||||
# Per JSON-RPC 2.0 (Response object, `id`), the error response must carry
|
||||
# the same id as the request when the id could be detected; null is only
|
||||
# for requests whose id could not be determined. `error_response` nils out
|
||||
# ids that fail validation, and the `:unknown_id` sentinel keeps a response
|
||||
# (with a null id) being emitted when the request carried no id at all.
|
||||
return error_response(id: id.nil? ? :unknown_id : id, id_validation_pattern: id_validation_pattern, error: {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: "Invalid Request",
|
||||
data: error,
|
||||
|
|
|
|||
Loading…
Reference in a new issue