[Bug]: A2A Server (Starlette) Fails with HTTP 503/ReadTimeout when Task Artifact Contains Multiple TextParts · Issue #112 · google-a2a/a2a-python · GitHub
More Web Proxy on the site http://driver.im/
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When an A2A agent's AgentExecutor produces a final result that includes an Artifact containing multiple a2a.types.TextPart objects, the A2A server (using A2AStarletteApplication and DefaultRequestHandler) fails to send a successful response to the client. Clients experience an httpx.ReadTimeout or an HTTP 503 error.
Server-side logs from the AgentExecutor show that it correctly processes the agent's output and calls TaskUpdater.add_artifact() with a list[Part] (containing multiple TextParts) and TaskUpdater.complete(). No exceptions are observed within the AgentExecutor itself.
The issue appears to occur later in the A2A framework, likely during the serialization of the a2a.types.Task object (which includes the multi-part artifact) into a JSON-RPC response, or during the HTTP sending phase by Starlette.
The problem is reproducible when an ADK agent using BuiltInCodeExecutor generates code, execution results, and a final text answer, all of which are converted into distinct TextParts for the final artifact.
If the AgentExecutor is modified to ensure the final artifact contains only a singleTextPart, the client receives a successful response. This points to the handling of multiple TextParts within an artifact as the root cause.
Steps to Reproduce:
Set up an A2A agent (like the code_interpreter_agent example) that uses an underlying mechanism (e.g., Google ADK with BuiltInCodeExecutor) which produces multiple distinct pieces of textual information for its final output (e.g., generated code, code output, final summary).
Ensure the AgentExecutor's convert_genai_parts_to_a2a (or equivalent) function converts these distinct pieces of information into multiple a2a.types.TextPart objects.
In the AgentExecutor._process_request method, when the final ADK event is received, create a list[Part] containing these multiple TextParts and pass it to TaskUpdater.add_artifact(). Then call TaskUpdater.complete().
Have an A2A client send a request to this agent.
Observe the client timeout / HTTP 503 error, and the lack of server-side exceptions after the AgentExecutor logs completion of its processing.
Expected Behavior: The A2A server should successfully serialize and send a Task object containing an artifact with multiple TextParts.
Actual Behavior: Client receives HTTP 503 / ReadTimeout. Server logs from the agent executor are clean, suggesting a failure later in the A2A framework's response pipeline.
Relevant log output
Server side:
NFO: Started server process [65049]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:10010 (Press CTRL+C to quit)
INFO: ::1:55023 - "GET /.well-known/agent.json HTTP/1.1" 200 OK
WARNING:google_genai.types:Warning: there are non-text parts in the response: ['executable_code', 'code_execution_result'],returning concatenated text result from text parts,check out the non text parts for full response from model.
DEBUG:adk_agent_executor:Received ADK event: content=Content(parts=[Part(video_metadata=None, thought=None, inline_data=None, code_execution_result=None, executable_code=ExecutableCode(code='print((5 + 7) * 3)\n', language=<Language.PYTHON: 'PYTHON'>), file_data=None, function_call=None, function_response=None, text=None), Part(video_metadata=None, thought=None, inline_data=None, code_execution_result=CodeExecutionResult(outcome=<Outcome.OUTCOME_OK: 'OUTCOME_OK'>, output='36\n'), executable_code=None, file_data=None, function_call=None, function_response=None, text=None), Part(video_metadata=None, thought=None, inline_data=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, text='36\n')], role='model') grounding_metadata=None partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=GenerateContentResponseUsageMetadata(cache_tokens_details=None, cached_content_token_count=None, candidates_token_count=18, candidates_tokens_details=[ModalityTokenCount(modality=<MediaModality.TEXT: 'TEXT'>, token_count=18)], prompt_token_count=95, prompt_tokens_details=[ModalityTokenCount(modality=<MediaModality.TEXT: 'TEXT'>, token_count=95)], thoughts_token_count=None, tool_use_prompt_token_count=110, tool_use_prompt_tokens_details=[ModalityTokenCount(modality=<MediaModality.TEXT: 'TEXT'>, token_count=110)], total_token_count=223, traffic_type=None) invocation_id='e-b5922d8e-65f8-431d-915e-e4c65b540dbb' author='code_interpreter_agent_adk' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='W4p38vWh' timestamp=1748281635.866301
DEBUG:adk_agent_executor:Agent generated code:
print((5+7) *3)DEBUG:adk_agent_executor:Code Execution Result: Outcome.OUTCOME_OK - Output:36DEBUG:adk_agent_executor:Text: '36'DEBUG:adk_agent_executor:Yielding final response: [TextPart(kind='text', metadata=None, text='[Generated Code Placeholder]'), TextPart(kind='text', metadata=None, text='[Code Result Placeholder: Outcome.OUTCOME_OK - 36]'), TextPart(kind='text', metadata=None, text='36')]DEBUG:adk_agent_executor:Yielding final response parts: [TextPart(kind='text', metadata=None, text='[Generated Code Placeholder]'), TextPart(kind='text', metadata=None, text='[Code Result Placeholder: Outcome.OUTCOME_OK - 36]'), TextPart(kind='text', metadata=None, text='36')]Client side:<user>% uv run test_client.pyINFO:__main__:Attempting to fetch public agent card from: http://localhost:10010/.well-known/agent.jsonINFO:httpx:HTTP Request: GET http://localhost:10010/.well-known/agent.json "HTTP/1.1200 OK"INFO:a2a.client.client:Successfully fetched agent card data from http://localhost:10010/.well-known/agent.json: {'capabilities': {'streaming': True}, 'defaultInputModes': ['text'], 'defaultOutputModes': ['text'], 'description': 'I can execute Python code to perform calculations and data manipulation.', 'name': 'ADK Code Interpreter Agent', 'skills': [{'description': 'Executes Python code to perform calculations and data manipulation.', 'examples': ['Calculate the value of (5 + 7) * 3', 'What is 10 factorial?', 'Solve for x: 2x + 5 = 15'], 'id': 'code_interpreter', 'name': 'Code Interpreter', 'tags': ['code', 'calculation', 'data manipulation']}], 'url': 'http://localhost:10010/', 'version': '1.0.0'}INFO:__main__:Successfully fetched public agent card:INFO:__main__:{ "capabilities": { "streaming": true }, "defaultInputModes": [ "text" ], "defaultOutputModes": [ "text" ], "description": "I can execute Python code to perform calculations and data manipulation.", "name": "ADK Code Interpreter Agent", "skills": [ { "description": "Executes Python code to perform calculations and data manipulation.", "examples": [ "Calculate the value of (5 + 7) * 3", "What is 10 factorial?", "Solve for x: 2x + 5 = 15" ], "id": "code_interpreter", "name": "Code Interpreter", "tags": [ "code", "calculation", "data manipulation" ] } ], "url": "http://localhost:10010/", "version": "1.0.0"}INFO:__main__:Using PUBLIC agent card for client initialization.INFO:__main__:A2AClient initialized.--- Sending query: 'Calculate the value of (5+7) *3' ---Traceback (most recent call last): File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_transports/default.py", line 101, in map_httpcore_exceptions yield File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_transports/default.py", line 394, in handle_async_request resp = await self._pool.handle_async_request(req) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/connection_pool.py", line 256, in handle_async_request raise exc from None File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/connection_pool.py", line 236, in handle_async_request response = await connection.handle_async_request( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pool_request.request ^^^^^^^^^^^^^^^^^^^^ ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/connection.py", line 103, in handle_async_request return await self._connection.handle_async_request(request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/http11.py", line 136, in handle_async_request raise exc File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/http11.py", line 106, in handle_async_request ) = await self._receive_response_headers(**kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/http11.py", line 177, in _receive_response_headers event = await self._receive_event(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_async/http11.py", line 217, in _receive_event data = await self._network_stream.read( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ self.READ_NUM_BYTES, timeout=timeout ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_backends/anyio.py", line 32, in read with map_exceptions(exc_map): ~~~~~~~~~~~~~~^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 162, in __exit__ self.gen.throw(value) ~~~~~~~~~~~~~~^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpcore/_exceptions.py", line 14, in map_exceptions raise to_exc(exc) from exchttpcore.ReadTimeoutThe above exception was the direct cause of the following exception:Traceback (most recent call last): File "/Users/<user>/a2asdkpython/a2a-python/src/a2a/client/client.py", line 279, in _send_request response = await self.httpx_client.post( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ self.url, json=rpc_request_payload, **(http_kwargs or {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1859, in post return await self.request( ^^^^^^^^^^^^^^^^^^^ ...<13 lines>... ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1540, in request return await self.send(request, auth=auth, follow_redirects=follow_redirects) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1629, in send response = await self._send_handling_auth( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<4 lines>... ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1657, in _send_handling_auth response = await self._send_handling_redirects( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...<3 lines>... ) ^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1694, in _send_handling_redirects response = await self._send_single_request(request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_client.py", line 1730, in _send_single_request response = await transport.handle_async_request(request) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_transports/default.py", line 393, in handle_async_request with map_httpcore_exceptions(): ~~~~~~~~~~~~~~~~~~~~~~~^^ File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 162, in __exit__ self.gen.throw(value) ~~~~~~~~~~~~~~^^^^^^^ File "/Users/<user>/a2asdkpython/a2a-python/.venv/lib/python3.13/site-packages/httpx/_transports/default.py", line 118, in map_httpcore_exceptions raise mapped_exc(message) from exchttpx.ReadTimeoutThe above exception was the direct cause of the following exception:Traceback (most recent call last): File "/Users/<user>/a2asdkpython/a2a-python/examples/google_adk/code_interpreter_agent/test_client.py", line 87, in <module> asyncio.run(main())~~~~~~~~~~~^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 195, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py", line 719, in run_until_complete
returnfuture.result()
~~~~~~~~~~~~~^^
File "/Users/<user>/a2asdkpython/a2a-python/examples/google_adk/code_interpreter_agent/test_client.py", line 69, in main
response = await client.send_message(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/<user>/a2asdkpython/a2a-python/src/a2a/utils/telemetry.py", line 158, in async_wrapper
result = await func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/<user>/a2asdkpython/a2a-python/src/a2a/client/client.py", line 199, in send_message
**await self._send_request(
^^^^^^^^^^^^^^^^^^^^^^^^^
...<2 lines>...
)
^
File "/Users/<user>/a2asdkpython/a2a-python/src/a2a/utils/telemetry.py", line 158, in async_wrapper
result = await func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/<user>/a2asdkpython/a2a-python/src/a2a/client/client.py", line 289, in _send_request
raise A2AClientHTTPError(
503, f'Network communication error: {e}'
) from e
a2a.client.errors.A2AClientHTTPError: HTTP Error 503: Network communication error:
Code of Conduct
I agree to follow this project's Code of Conduct
The text was updated successfully, but these errors were encountered:
What happened?
When an A2A agent's
AgentExecutor
produces a final result that includes anArtifact
containing multiplea2a.types.TextPart
objects, the A2A server (usingA2AStarletteApplication
andDefaultRequestHandler
) fails to send a successful response to the client. Clients experience anhttpx.ReadTimeout
or an HTTP 503 error.Server-side logs from the
AgentExecutor
show that it correctly processes the agent's output and callsTaskUpdater.add_artifact()
with alist[Part]
(containing multipleTextPart
s) andTaskUpdater.complete()
. No exceptions are observed within theAgentExecutor
itself.The issue appears to occur later in the A2A framework, likely during the serialization of the
a2a.types.Task
object (which includes the multi-part artifact) into a JSON-RPC response, or during the HTTP sending phase by Starlette.The problem is reproducible when an ADK agent using
BuiltInCodeExecutor
generates code, execution results, and a final text answer, all of which are converted into distinctTextPart
s for the final artifact.If the
AgentExecutor
is modified to ensure the final artifact contains only a singleTextPart
, the client receives a successful response. This points to the handling of multipleTextPart
s within an artifact as the root cause.Steps to Reproduce:
code_interpreter_agent
example) that uses an underlying mechanism (e.g., Google ADK withBuiltInCodeExecutor
) which produces multiple distinct pieces of textual information for its final output (e.g., generated code, code output, final summary).AgentExecutor
'sconvert_genai_parts_to_a2a
(or equivalent) function converts these distinct pieces of information into multiplea2a.types.TextPart
objects.AgentExecutor._process_request
method, when the final ADK event is received, create alist[Part]
containing these multipleTextPart
s and pass it toTaskUpdater.add_artifact()
. Then callTaskUpdater.complete()
.AgentExecutor
logs completion of its processing.Expected Behavior: The A2A server should successfully serialize and send a
Task
object containing an artifact with multipleTextPart
s.Actual Behavior: Client receives HTTP 503 / ReadTimeout. Server logs from the agent executor are clean, suggesting a failure later in the A2A framework's response pipeline.
Relevant log output
Code of Conduct
The text was updated successfully, but these errors were encountered: