Skip to content

Unify JSON-RPC message validation policy across transports #211

Description

@benbrandt

Follow-up from #206 / PR #210.

Message validation currently differs by transport:

  • ndjson (stdio): ndJsonStream skips non-object JSON lines and passes any object-shaped message to the connection layer, which handles lenient shapes (resolves responses carrying error: null alongside result, fast-rejects structurally invalid responses with RequestError.invalidRequest, dispatches requests missing the jsonrpc field).
  • WebSocket / SSE: ws-stream.ts:205, ws-server.ts:144, and sse.ts validate with the strict isJsonRpcMessage guard and silently drop anything that fails it.

Consequences of the strict-drop policy on WS/SSE:

  1. A lenient peer's response (e.g. {"jsonrpc":"2.0","id":1,"result":{...},"error":null}) is dropped, so the caller's sendRequest promise never settles — there is no request timeout, so it hangs until the connection closes. The same message works over stdio.
  2. A post-initialize request that fails validation gets neither processing nor the spec-mandated -32600 error response, so the remote caller hangs too. Only the connection layer (which owns responders) can generate that reply.

PR #210 hardened the connection layer (receiveMessage guards non-objects; handleResponse fast-rejects malformed error members), so a reasonable resolution is to relax the per-transport guards to an object check and let Connection own the accept/reject policy in one place — ideally answering invalid requests with RequestError.invalidRequest instead of silently discarding them.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions