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
{{ message }}
Repository navigation
Python: [Bug]: A2AExecutor drops image, file and data parts of incoming A2A messages #9566
A2AExecutor (agent-framework-a2a) passes only the text of the incoming A2A message to the agent.
Raw (bytes), URL and data parts are dropped.
A client sends "What is in this picture?" plus a PNG. The agent receives only the string "What is in this picture?" and never sees the image.
A client sends only a file (for example a PDF). The agent is called with an empty string "".
This also breaks the framework's own round trip: A2AAgent sends Content.from_data(...) / Content.from_uri(...) as raw and URL parts, and an A2AExecutor server throws them away.
The class docstring says the executor supports "text, binary data, and URI-based content", but that is only true for output.
Expected Behavior
Every part of the incoming message reaches the agent: text as text, raw parts as data content, URL parts as URI content, with media type and filename kept.
A text-only message can keep being passed as plain text.
Steps to Reproduce
Wrap any agent in A2AExecutor.
Call execute() with a RequestContext whose message has a text part and a raw image part.
Look at what the agent's run() receives.
Minimal Reproduction
importasynciofroma2a.server.agent_executionimportRequestContextfroma2a.server.contextimportServerCallContextfroma2a.server.eventsimportEventQueueLegacyfroma2a.typesimportMessageasA2AMessagefroma2a.typesimportPart, Role, SendMessageRequestfromagent_frameworkimportAgentResponse, AgentSession, Messagefromagent_framework.a2aimportA2AExecutorclassRecordingAgent:
"""Stands in for any agent and records what the executor passes to run()."""id="recording"name="recording"description=Nonedefcreate_session(self, **kwargs):
returnAgentSession(**kwargs)
defget_session(self, service_session_id, **kwargs):
returnAgentSession(service_session_id=service_session_id, **kwargs)
asyncdefrun(self, messages=None, **kwargs):
self.received=messagesreturnAgentResponse(messages=[Message(role="assistant", contents=["ok"])])
asyncdefmain() ->None:
agent=RecordingAgent()
incoming=A2AMessage(
message_id="m1",
role=Role.ROLE_USER,
parts=[
Part(text="What is in this picture?"),
Part(raw=b"\x89PNG\r\n\x1a\nfake", media_type="image/png", filename="cat.png"),
Part(url="https://example.com/report.pdf", media_type="application/pdf"),
],
)
context=RequestContext(
ServerCallContext(),
request=SendMessageRequest(message=incoming),
task_id="t1",
context_id="c1",
)
awaitA2AExecutor(agent).execute(context, EventQueueLegacy())
print("agent.run received:", repr(agent.received))
asyncio.run(main())
agent-framework-core: 1.22.0, agent-framework-a2a: 1.0.0b261015 (main at 08851e6), a2a-sdk: 1.2.1
Python Version
Python 3.14.5
Operating System
macOS
Regression
No
Additional Context
Root cause: python/packages/a2a/agent_framework_a2a/_a2a_executor.py, A2AExecutor.execute, line 206 on main:
query=context.get_user_input()
RequestContext.get_user_input() in a2a-sdk joins only the text parts ("Extracts text content from the user's message parts"). That string is what _run / _run_stream pass to agent.run(), so all other parts are lost before the agent is called.
The newer agent-framework-hosting-a2a package already converts URL and raw parts into Content (_conversion.py), and A2AAgent._parse_contents_from_a2a does the same on the client side. Only A2AExecutor uses the text-only path.
Suggested fix: when the message has any non-text part, build a user Message from all parts (text, raw → data, url → uri, data → JSON text, keeping media_type and filename). Keep passing the joined text for text-only messages so existing behavior does not change. I have this with four tests using a real a2a-sdk RequestContext (text plus image, file only, data part, and a text-only guard); the first three fail on main and pass with the fix. Happy to open the PR once this is agreed.
AI assistance: found and reproduced with an AI coding assistant; I ran the repro and checked the code myself.
Acknowledgements
I searched existing issues and did not find a duplicate.
I personally verified this behavior and the reproduction details are authentic.
I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.
🤖 Automated triage reproduction notes(agent-authored — trust but verify)
Agent analysis
Repro: python/packages/a2a/agent_framework_a2a/_a2a_executor.py::A2AExecutor.execute line 206 reduces incoming messages to context.get_user_input(), so raw, URL, and data parts are absent from both _run and _run_stream. Minimal repro: execute a real RequestContext containing text, raw, URL, and data parts and assert that the first argument passed to agent.run() is a framework Message; it is currently only the text string.
Activity
github-actions commentedon Oct 20, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
Repro:
python/packages/a2a/agent_framework_a2a/_a2a_executor.py::A2AExecutor.executeline 206 reduces incoming messages tocontext.get_user_input(), so raw, URL, and data parts are absent from both_runand_run_stream. Minimal repro: execute a realRequestContextcontaining text, raw, URL, and data parts and assert that the first argument passed toagent.run()is a frameworkMessage; it is currently only the text string.python/packages/a2a/tests/test_a2a_executor.py1.0.0b2610151.0.0b261015