Skip to content

Commit 9cd4693

Browse files
fix(agents): tolerate empty A2A message parts in RemoteA2aAgent
Guard against empty event.content.parts when handling initial TASK responses to avoid IndexError. Add unit test covering empty-parts TASK_REQUIRED scenario.
1 parent 9bb5079 commit 9cd4693

2 files changed

Lines changed: 2 additions & 53 deletions

File tree

src/google/adk/agents/remote_a2a_agent.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,9 @@ async def _handle_a2a_response(
421421
task, self.name, ctx, self._a2a_part_converter
422422
)
423423
# for streaming task, we update the event with the task status.
424-
# We update the event as Thought updates. Mark all parts as thought
425-
# when the task is in submitted state. This handles multi-part
426-
# messages and avoids IndexError when parts is empty.
424+
# We update the event as Thought updates.
427425
if task and task.status and task.status.state == TaskState.submitted:
428-
if event.content and event.content.parts:
429-
for part in event.content.parts:
430-
part.thought = True
426+
event.content.parts[0].thought = True
431427
elif (
432428
isinstance(update, A2ATaskStatusUpdateEvent)
433429
and update.status

tests/unittests/agents/test_remote_a2a_agent.py

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -770,53 +770,6 @@ async def test_handle_a2a_response_with_task_completed_and_no_update(self):
770770
assert A2A_METADATA_PREFIX + "task_id" in result.custom_metadata
771771
assert A2A_METADATA_PREFIX + "context_id" in result.custom_metadata
772772

773-
@pytest.mark.asyncio
774-
async def test_handle_a2a_response_with_task_submitted_no_parts(self):
775-
"""Test handling of TASK_REQUIRED response with no message parts.
776-
777-
This test verifies that the code correctly handles the A2A protocol
778-
scenario where a streaming task returns with status.state == submitted
779-
but has an empty parts list (no message content). This is valid per
780-
the A2A protocol specification.
781-
"""
782-
mock_a2a_task = Mock(spec=A2ATask)
783-
mock_a2a_task.id = "task-123"
784-
mock_a2a_task.context_id = "context-123"
785-
mock_a2a_task.status = Mock(spec=A2ATaskStatus)
786-
mock_a2a_task.status.state = TaskState.submitted
787-
788-
# Create an Event with empty parts list (valid per A2A protocol)
789-
mock_event = Event(
790-
author=self.agent.name,
791-
invocation_id=self.mock_context.invocation_id,
792-
branch=self.mock_context.branch,
793-
content=genai_types.Content(role="model", parts=[]),
794-
)
795-
796-
with patch(
797-
"google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
798-
) as mock_convert:
799-
mock_convert.return_value = mock_event
800-
801-
# This should not raise IndexError
802-
result = await self.agent._handle_a2a_response(
803-
(mock_a2a_task, None), self.mock_context
804-
)
805-
806-
assert result == mock_event
807-
mock_convert.assert_called_once_with(
808-
mock_a2a_task,
809-
self.agent.name,
810-
self.mock_context,
811-
self.mock_a2a_part_converter,
812-
)
813-
# Verify that the empty parts list is left unchanged
814-
assert result.content.parts == []
815-
# Check that metadata was added
816-
assert result.custom_metadata is not None
817-
assert A2A_METADATA_PREFIX + "task_id" in result.custom_metadata
818-
assert A2A_METADATA_PREFIX + "context_id" in result.custom_metadata
819-
820773
def test_construct_message_parts_from_session_preserves_order(self):
821774
"""Test that message parts are in correct order with multi-part messages.
822775

0 commit comments

Comments
 (0)