Skip to content

[Bug]: Server does not validate A2A-Version header — missing VersionNotSupportedError for unsupported versions #856

Closed

Description

@jmesnil

What happened?

Server does not validate A2A-Version header — missing VersionNotSupportedError for unsupported versions

Summary

The a2a-python SDK server does not validate the A2A-Version request header. Requests with unsupported versions (e.g., 99.0) are processed normally instead of returning a VersionNotSupportedError, failing on both JSON-RPC and HTTP+JSON transports.

Requirement

Specification

Agents MUST process requests using the semantics of the requested A2A-Version (matching Major.Minor). If the version is not supported by the interface, agents MUST return a VersionNotSupportedError.

Expected behavior

When a request includes A2A-Version: 99.0, the server should reject it with a VersionNotSupportedError:

  • JSON-RPC: error response with code -32009
  • HTTP+JSON: HTTP 400 Bad Request

Actual behavior

The server ignores the A2A-Version header entirely and processes the request normally, returning a successful result (HTTP 200) on both transports. The SDK defines VersionNotSupportedError and its error code mappings (src/a2a/utils/errors.py) but the server request handlers (jsonrpc_handler.py, rest_handler.py) never read or validate the header.

Reproducer

# 1. Verify the agent is running
curl -s http://localhost:9999/.well-known/agent-card.json | jq '.supportedInterfaces'

# 2. JSON-RPC: Send a request with unsupported A2A-Version 99.0
#    Expected: JSON-RPC error with code -32009
#    Actual: successful result with task data
curl -s -X POST http://localhost:9999 \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 99.0" \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"role":"ROLE_USER","parts":[{"text":"version test"}],"messageId":"ver-repro-001"}}}' | jq .

# 3. HTTP+JSON: Send a request with unsupported A2A-Version 99.0
#    Expected: HTTP 400 with VersionNotSupportedError
#    Actual: HTTP 200 with successful task result
curl -s -w "\nHTTP_STATUS: %{http_code}\n" -X POST http://localhost:9999/a2a/rest/message:send \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 99.0" \
  -d '{"message":{"role":"ROLE_USER","parts":[{"text":"version test"}],"messageId":"ver-repro-002"}}'

TCK test

tests/compatibility/core_operations/test_error_handling.py::TestVersionErrors::test_unsupported_version_returns_error_jsonrpc
tests/compatibility/core_operations/test_error_handling.py::TestVersionErrors::test_unsupported_version_returns_error_rest

Relevant log output

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. added theissue type on Mar 17, 2026
  2. jmesnil commented on Mar 17, 2026

    @jmesnil
    Author

    I'm testing the a2a-python SDK from the v1.0.0-alpha.0 tag.

    This is an ongoing task for the a2a-tck so I need to make sure that the SUT is properly implemented (it's in the PR at https://github.com/a2aproject/a2a-tck/pull/139/changes#diff-c45a7225191a2ae4f3bd7af29c3036b9e461ee93d3d31964aed417d46086d637) but this looks to be a genuine error.

  3. added
    component: serverIssues related to frameworks for agent execution, HTTP/event handling, database persistence logic.
    on Apr 2, 2026
  4. mtsmyassin commented on Apr 17, 2026

    @mtsmyassin

    What happened?

    Server does not validate A2A-Version header — missing VersionNotSupportedError for unsupported versions

    Summary

    The a2a-python SDK server does not validate the A2A-Version request header. Requests with unsupported versions (e.g., 99.0) are processed normally instead of returning a VersionNotSupportedError, failing on both JSON-RPC and HTTP+JSON transports.

    Requirement

    Specification

    Agents MUST process requests using the semantics of the requested A2A-Version (matching Major.Minor). If the version is not supported by the interface, agents MUST return a VersionNotSupportedError.

    Expected behavior

    When a request includes A2A-Version: 99.0, the server should reject it with a VersionNotSupportedError:

    • JSON-RPC: error response with code -32009
    • HTTP+JSON: HTTP 400 Bad Request

    Actual behavior

    The server ignores the A2A-Version header entirely and processes the request normally, returning a successful result (HTTP 200) on both transports. The SDK defines VersionNotSupportedError and its error code mappings (src/a2a/utils/errors.py) but the server request handlers (jsonrpc_handler.py, rest_handler.py) never read or validate the header.

    Reproducer

    1. Verify the agent is running

    curl -s http://localhost:9999/.well-known/agent-card.json | jq '.supportedInterfaces'

    2. JSON-RPC: Send a request with unsupported A2A-Version 99.0

    Expected: JSON-RPC error with code -32009

    Actual: successful result with task data

    curl -s -X POST http://localhost:9999
    -H "Content-Type: application/json"
    -H "A2A-Version: 99.0"
    -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"role":"ROLE_USER","parts":[{"text":"version test"}],"messageId":"ver-repro-001"}}}' | jq .

    3. HTTP+JSON: Send a request with unsupported A2A-Version 99.0

    Expected: HTTP 400 with VersionNotSupportedError

    Actual: HTTP 200 with successful task result

    curl -s -w "\nHTTP_STATUS: %{http_code}\n" -X POST http://localhost:9999/a2a/rest/message:send
    -H "Content-Type: application/json"
    -H "A2A-Version: 99.0"
    -d '{"message":{"role":"ROLE_USER","parts":[{"text":"version test"}],"messageId":"ver-repro-002"}}'

    TCK test

    tests/compatibility/core_operations/test_error_handling.py::TestVersionErrors::test_unsupported_version_returns_error_jsonrpc
    tests/compatibility/core_operations/test_error_handling.py::TestVersionErrors::test_unsupported_version_returns_error_rest
    

    Relevant log output

    Code of Conduct

    • I agree to follow this project's Code of Conduct

    oposed Implementation

    Here's a concrete implementation for A2A-Version header validation as a Starlette middleware. This can be added to JSONRPCApplication / A2AStarletteApplication:

    # src/a2a/server/apps/jsonrpc/version_middleware.py
    
    from collections.abc import Callable
    from starlette.middleware.base import BaseHTTPMiddleware
    from starlette.requests import Request
    from starlette.responses import JSONResponse, Response
    import json
    
    # The versions supported by this SDK
    SUPPORTED_A2A_VERSIONS = {"1.0"}
    A2A_VERSION_HEADER = "A2A-Version"
    
    
    class A2AVersionMiddleware(BaseHTTPMiddleware):
        """Middleware that validates the A2A-Version request header.
    
        Per the A2A specification, servers MUST return a VersionNotSupportedError
        (-32001) if the client requests a version the server does not support.
        If the header is absent, the server should assume the latest supported version.
        """
    
        def __init__(self, app, supported_versions: set[str] = SUPPORTED_A2A_VERSIONS):
            super().__init__(app)
            self.supported_versions = supported_versions
    
        async def dispatch(self, request: Request, call_next: Callable) -> Response:
            version = request.headers.get(A2A_VERSION_HEADER)
            if version is not None and version not in self.supported_versions:
                # Return a JSON-RPC-style error response
                error_body = {
                    "jsonrpc": "2.0",
                    "id": None,
                    "error": {
                        "code": -32001,
                        "message": f"A2A version '{version}' is not supported. "
                                   f"Supported versions: {sorted(self.supported_versions)}",
                    },
                }
                return JSONResponse(
                    content=error_body,
                    status_code=400,
                    headers={"A2A-Version": next(iter(sorted(self.supported_versions, reverse=True)))},
                )
            return await call_next(request)

    Then wire it into the app:

    # In JSONRPCApplication / A2AStarletteApplication build() or __init__:
    
    from starlette.applications import Starlette
    from starlette.middleware import Middleware
    from a2a.server.apps.jsonrpc.version_middleware import A2AVersionMiddleware
    
    app = Starlette(
        routes=routes,
        middleware=[
            Middleware(A2AVersionMiddleware, supported_versions={"1.0"}),
        ],
    )

    Notes on spec compliance

    Looking at the A2A spec, the VersionNotSupportedError is error code -32001. The middleware above returns this in the JSON-RPC envelope.

    The spec also says the server SHOULD include an A2A-Version header in its responses indicating the version it used. The middleware response above includes it, and it would be good to add it to all other responses as well (perhaps in a separate response middleware or via a send wrapper).

    Testing

    # Test that an unsupported version is rejected
    response = client.post(
        "/",
        json={"jsonrpc": "2.0", "method": "message/send", "id": 1, "params": {...}},
        headers={"A2A-Version": "0.9"},
    )
    assert response.status_code == 400
    assert response.json()["error"]["code"] == -32001
    
    # Test that a supported version passes through
    response = client.post(
        "/",
        json={"jsonrpc": "2.0", "method": "message/send", "id": 1, "params": {...}},
        headers={"A2A-Version": "1.0"},
    )
    assert response.status_code == 200
    
    # Test that missing header is accepted (backwards compat)
    response = client.post(
        "/",
        json={"jsonrpc": "2.0", "method": "message/send", "id": 1, "params": {...}},
    )
    assert response.status_code == 200

    Happy to submit a PR if useful.

  5. ishymko commented on Apr 23, 2026

    @ishymko
    Member

    Done in #865.

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

Metadata

Metadata

Assignees

Labels

component: serverIssues related to frameworks for agent execution, HTTP/event handling, database persistence logic.maintainers-only

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions