rohitg00/ai-engineering-from-scratch · error · ProtocolViolation

response id does not exactly match request id

Error message

response id does not exactly match request id

What it means

The JSON-RPC id correlation check in validate_response. It compares request and response ids with both an exact type check and an equality check, so a response id of True matches a request id of 1 only by value in Python but is rejected because type(response_id) is not type(request_id). Fires when the server echoes the wrong id, coerces it to a string, or answers a different request.

Solutions

  1. Echo the request id verbatim in the response, preserving its type (int, string, or null)
  2. Ensure the server does not stringify or renumber ids when routing batch replies
  3. Match the response to the correct in-flight request before validating
  4. In tests, build the expected response from the same id object used in the request
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at phases/13-tools-and-protocols/31-mcp-conformance-versioning-and-operations/code/main.py:393 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/5bb7318b40dc5bfe. Report an issue: GitHub.

Appendix: source

Thrown at phases/13-tools-and-protocols/31-mcp-conformance-versioning-and-operations/code/main.py:393

    name: str
    passed: bool
    detail: str
    evidence_digest: str


def validate_response(
    request: dict[str, Any],
    status: int,
    response: dict[str, Any],
    era: str,
    capabilities: set[str],
) -> None:
    if not isinstance(response, dict) or response.get("jsonrpc") != "2.0":
        raise ProtocolViolation("invalid JSON-RPC response envelope")
    request_id = request.get("id")
    response_id = response.get("id")
    if type(response_id) is not type(request_id) or response_id != request_id:
        raise ProtocolViolation("response id does not exactly match request id")
    has_result = "result" in response
    has_error = "error" in response
    if has_result == has_error:
        raise ProtocolViolation("response must contain exactly one of result or error")
    if has_result:
        if status != 200:
            raise ProtocolViolation("successful JSON-RPC result requires HTTP 200")
        validate_result(response["result"], era, capabilities)
        validate_method_result(request["method"], response["result"])
        return

    error = response["error"]
    if not isinstance(error, dict):
        raise ProtocolViolation("JSON-RPC error must be an object")
    code = error.get("code")
    if isinstance(code, bool) or not isinstance(code, int) or not isinstance(error.get("message"), str):
        raise ProtocolViolation("JSON-RPC error requires integer code and string message")
    expected_status = {-32020: 400, -32021: 400, -32022: 400, -32601: 404}.get(code)

View on GitHub (pinned to 39ea8a1c6d)