Skip to content

fix(server): reject boolean JSON-RPC request IDs - #1350

Open
ningyixi wants to merge 2 commits into
a2aproject:mainfrom
ningyixi:fix/boolean-jsonrpc-request-ids
Open

ningyixi wants to merge 2 commits into
a2aproject:mainfrom
ningyixi:fix/boolean-jsonrpc-request-ids

Conversation

@ningyixi

Copy link
Copy Markdown

Description

JSON-RPC requests with id: true or id: false currently reach the request handler and produce responses containing the same boolean ID. Python's bool satisfies the existing integer check, but JSON-RPC 2.0 distinguishes Boolean from Number and permits only String, Number or Null identifiers.

Reject boolean IDs before dispatch with Invalid Request (-32600) and a null response ID. Add request-level tests for both boolean values and passing controls for integer zero, a positive integer, an empty string and a nonempty string. The invalid requests must not invoke the handler.

  • Follow the CONTRIBUTING guide.
  • Use a Conventional Commits PR title.
  • Ensure tests and linter pass.
  • Update appropriate docs if necessary. No documentation change is needed; this enforces the existing JSON-RPC identifier contract.

No existing issue found for boolean request IDs.

Validation:

  • On the unchanged base, both boolean regressions fail and all four valid-ID controls pass (initial reproduction on Python 3.12).
  • bash scripts/lint.sh: Ruff lint/format and ty pass. ty reports three unused-ignore warnings in unchanged optional gRPC imports.
  • uv run pytest: 2,290 passed, 183 skipped, 3 xfailed and 1 xpassed on the repository-pinned Python 3.10.22.
  • uv run pytest --cov=src --cov-report=term-missing --cov-fail-under=88: same results; 93.29% coverage, exceeding the 88% requirement.
  • git diff --check: passed.

The xfail/xpass cases are marked against #869. Skips include PostgreSQL/MySQL and externally configured multi-server cases. Local HTTP/gRPC and sample integration tests ran as part of the full suite; no live model-provider calls were used.

AI assistance: OpenAI Codex implementation, reproduction and validation.

@ningyixi
ningyixi requested a review from a team as a code owner October 17, 2026 15:32
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/routes/jsonrpc_dispatcher.py 85.50% 86.47% 🟢 +0.97%
Total 92.90% 92.92% 🟢 +0.02%

Generated by coverage-comment.yml

@mykytanetipa mykytanetipa self-assigned this Oct 19, 2026
return self._generate_error_response(
None,
InvalidRequestError(
message='Invalid request: id must be a string, number, or null'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets have "integer" instead of "number" since float values are also against spec

Comment on lines +236 to +242
if isinstance(request_id, bool):
return self._generate_error_response(
None,
InvalidRequestError(
message='Invalid request: id must be a string, number, or null'
),
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

design comment: I'd move this check to the "# 1) Validate base JSON-RPC structure only (-32600 on failure)" validate step, because right now it skips the request body logging + placed separately from the rest of request body validations.

smth like this:

body = await request.json()
if isinstance(body, dict):
    request_id = body.get('id')
    # Ensure request_id is valid for JSON-RPC response (str/int/None only;
    # exclude bool since bool is a subclass of int in Python)
    if isinstance(request_id, bool) or (
        request_id is not None
        and not isinstance(request_id, str | int)
    ):
        request_id = None
logger.debug('Request body: %s', body)
# 1) Validate base JSON-RPC structure only (-32600 on failure)
try:
    base_request = JSONRPC20Request.from_data(body)
    if not isinstance(base_request, JSONRPC20Request):
        ...
    if body.get('jsonrpc') != '2.0':
        ...
    if isinstance(base_request._id, bool):  # noqa: SLF001
        return self._generate_error_response(
            request_id,
            InvalidRequestError(
                message='Invalid request: id must be a string, integer, or null'
            ),
        )

Move the bool-id rejection into the base JSON-RPC validation step and
say integer instead of number in the error message, per review on a2aproject#1350.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants