2929from a2a .types import AgentSkill
3030from a2a .types import Artifact
3131from a2a .types import Message as A2AMessage
32- from a2a .types import Part as A2ATaskStatus
3332from a2a .types import SendMessageSuccessResponse
3433from a2a .types import Task as A2ATask
3534from a2a .types import TaskArtifactUpdateEvent
3635from a2a .types import TaskState
37- from a2a .types import TaskStatus
36+ from a2a .types import TaskStatus as A2ATaskStatus
3837from a2a .types import TaskStatusUpdateEvent
3938from a2a .types import TextPart
4039from google .adk .agents .invocation_context import InvocationContext
4140from google .adk .agents .remote_a2a_agent import A2A_METADATA_PREFIX
4241from google .adk .agents .remote_a2a_agent import AgentCardResolutionError
4342from google .adk .agents .remote_a2a_agent import RemoteA2aAgent
43+ import google .adk .agents .remote_a2a_agent as remote_a2a_agent
4444from google .adk .events .event import Event
4545from google .adk .sessions .session import Session
4646from google .genai import types as genai_types
@@ -579,7 +579,7 @@ def test_create_a2a_request_for_user_function_response_success(self):
579579 "google.adk.agents.remote_a2a_agent.convert_event_to_a2a_message"
580580 ) as mock_convert :
581581 # Create a proper mock A2A message
582- mock_a2a_message = Mock ( spec = A2AMessage )
582+ mock_a2a_message = create_autospec ( A2AMessage , instance = True )
583583 mock_a2a_message .task_id = None # Will be set by the method
584584 mock_convert .return_value = mock_a2a_message
585585
@@ -716,8 +716,10 @@ async def test_handle_a2a_response_with_task_completed_and_no_update(self):
716716 content = genai_types .Content (role = "model" , parts = [mock_a2a_part ]),
717717 )
718718
719- with patch (
720- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
719+ with patch .object (
720+ remote_a2a_agent ,
721+ "convert_a2a_task_to_event" ,
722+ autospec = True ,
721723 ) as mock_convert :
722724 mock_convert .return_value = mock_event
723725
@@ -821,8 +823,10 @@ async def test_handle_a2a_response_with_task_submitted_and_no_update(self):
821823 content = genai_types .Content (role = "model" , parts = [mock_a2a_part ]),
822824 )
823825
824- with patch (
825- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
826+ with patch .object (
827+ remote_a2a_agent ,
828+ "convert_a2a_task_to_event" ,
829+ autospec = True ,
826830 ) as mock_convert :
827831 mock_convert .return_value = mock_event
828832
@@ -845,6 +849,59 @@ async def test_handle_a2a_response_with_task_submitted_and_no_update(self):
845849 assert A2A_METADATA_PREFIX + "task_id" in result .custom_metadata
846850 assert A2A_METADATA_PREFIX + "context_id" in result .custom_metadata
847851
852+ @pytest .mark .asyncio
853+ @pytest .mark .parametrize (
854+ "task_state,event_content" ,
855+ [
856+ pytest .param (
857+ TaskState .submitted ,
858+ genai_types .Content (role = "model" , parts = []),
859+ id = "submitted_empty_parts" ,
860+ ),
861+ pytest .param (
862+ TaskState .working ,
863+ None ,
864+ id = "working_no_content" ,
865+ ),
866+ ],
867+ )
868+ async def test_handle_a2a_response_with_task_missing_content (
869+ self , task_state , event_content
870+ ):
871+ """Test streaming A2A response handling when content/parts are missing.
872+
873+ This verifies the fix for issue #3769 where the code could raise when it
874+ tried to read parts[0] without checking for empty/missing content.
875+ """
876+ mock_a2a_task = create_autospec (A2ATask , instance = True )
877+ mock_a2a_task .id = "task-123"
878+ mock_a2a_task .context_id = "context-123"
879+ mock_a2a_task .status = create_autospec (A2ATaskStatus , instance = True )
880+ mock_a2a_task .status .state = task_state
881+
882+ mock_event = Event (
883+ author = self .agent .name ,
884+ invocation_id = self .mock_context .invocation_id ,
885+ branch = self .mock_context .branch ,
886+ content = event_content ,
887+ )
888+
889+ with patch .object (
890+ remote_a2a_agent ,
891+ "convert_a2a_task_to_event" ,
892+ autospec = True ,
893+ ) as mock_convert :
894+ mock_convert .return_value = mock_event
895+
896+ result = await self .agent ._handle_a2a_response (
897+ (mock_a2a_task , None ), self .mock_context
898+ )
899+
900+ assert result == mock_event
901+ assert result .custom_metadata is not None
902+ assert A2A_METADATA_PREFIX + "task_id" in result .custom_metadata
903+ assert A2A_METADATA_PREFIX + "context_id" in result .custom_metadata
904+
848905 @pytest .mark .asyncio
849906 async def test_handle_a2a_response_with_task_working_and_no_update (self ):
850907 """Test successful A2A response handling with streaming task and no update."""
@@ -863,8 +920,10 @@ async def test_handle_a2a_response_with_task_working_and_no_update(self):
863920 content = genai_types .Content (role = "model" , parts = [mock_a2a_part ]),
864921 )
865922
866- with patch (
867- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
923+ with patch .object (
924+ remote_a2a_agent ,
925+ "convert_a2a_task_to_event" ,
926+ autospec = True ,
868927 ) as mock_convert :
869928 mock_convert .return_value = mock_event
870929
@@ -896,7 +955,7 @@ async def test_handle_a2a_response_with_task_status_update_with_message(self):
896955
897956 mock_a2a_message = Mock (spec = A2AMessage )
898957 mock_update = Mock (spec = TaskStatusUpdateEvent )
899- mock_update .status = Mock (TaskStatus )
958+ mock_update .status = Mock (A2ATaskStatus )
900959 mock_update .status .state = TaskState .completed
901960 mock_update .status .message = mock_a2a_message
902961
@@ -942,7 +1001,7 @@ async def test_handle_a2a_response_with_task_status_working_update_with_message(
9421001
9431002 mock_a2a_message = Mock (spec = A2AMessage )
9441003 mock_update = Mock (spec = TaskStatusUpdateEvent )
945- mock_update .status = Mock (TaskStatus )
1004+ mock_update .status = Mock (A2ATaskStatus )
9461005 mock_update .status .state = TaskState .working
9471006 mock_update .status .message = mock_a2a_message
9481007
@@ -984,7 +1043,7 @@ async def test_handle_a2a_response_with_task_status_update_no_message(self):
9841043 mock_a2a_task .id = "task-123"
9851044
9861045 mock_update = Mock (spec = TaskStatusUpdateEvent )
987- mock_update .status = Mock (TaskStatus )
1046+ mock_update .status = Mock (A2ATaskStatus )
9881047 mock_update .status .state = TaskState .completed
9891048 mock_update .status .message = None
9901049
@@ -1014,8 +1073,10 @@ async def test_handle_a2a_response_with_artifact_update(self):
10141073 branch = self .mock_context .branch ,
10151074 )
10161075
1017- with patch (
1018- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
1076+ with patch .object (
1077+ remote_a2a_agent ,
1078+ "convert_a2a_task_to_event" ,
1079+ autospec = True ,
10191080 ) as mock_convert :
10201081 mock_convert .return_value = mock_event
10211082
@@ -1222,8 +1283,10 @@ async def test_handle_a2a_response_with_task_completed_and_no_update(self):
12221283 content = genai_types .Content (role = "model" , parts = [mock_a2a_part ]),
12231284 )
12241285
1225- with patch (
1226- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
1286+ with patch .object (
1287+ remote_a2a_agent ,
1288+ "convert_a2a_task_to_event" ,
1289+ autospec = True ,
12271290 ) as mock_convert :
12281291 mock_convert .return_value = mock_event
12291292
@@ -1263,8 +1326,10 @@ async def test_handle_a2a_response_with_task_submitted_and_no_update(self):
12631326 content = genai_types .Content (role = "model" , parts = [mock_a2a_part ]),
12641327 )
12651328
1266- with patch (
1267- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
1329+ with patch .object (
1330+ remote_a2a_agent ,
1331+ "convert_a2a_task_to_event" ,
1332+ autospec = True ,
12681333 ) as mock_convert :
12691334 mock_convert .return_value = mock_event
12701335
@@ -1296,7 +1361,7 @@ async def test_handle_a2a_response_with_task_status_update_with_message(self):
12961361
12971362 mock_a2a_message = Mock (spec = A2AMessage )
12981363 mock_update = Mock (spec = TaskStatusUpdateEvent )
1299- mock_update .status = Mock (TaskStatus )
1364+ mock_update .status = Mock (A2ATaskStatus )
13001365 mock_update .status .state = TaskState .completed
13011366 mock_update .status .message = mock_a2a_message
13021367
@@ -1342,7 +1407,7 @@ async def test_handle_a2a_response_with_task_status_working_update_with_message(
13421407
13431408 mock_a2a_message = Mock (spec = A2AMessage )
13441409 mock_update = Mock (spec = TaskStatusUpdateEvent )
1345- mock_update .status = Mock (TaskStatus )
1410+ mock_update .status = Mock (A2ATaskStatus )
13461411 mock_update .status .state = TaskState .working
13471412 mock_update .status .message = mock_a2a_message
13481413
@@ -1384,7 +1449,7 @@ async def test_handle_a2a_response_with_task_status_update_no_message(self):
13841449 mock_a2a_task .id = "task-123"
13851450
13861451 mock_update = Mock (spec = TaskStatusUpdateEvent )
1387- mock_update .status = Mock (TaskStatus )
1452+ mock_update .status = Mock (A2ATaskStatus )
13881453 mock_update .status .state = TaskState .completed
13891454 mock_update .status .message = None
13901455
@@ -1414,8 +1479,10 @@ async def test_handle_a2a_response_with_artifact_update(self):
14141479 branch = self .mock_context .branch ,
14151480 )
14161481
1417- with patch (
1418- "google.adk.agents.remote_a2a_agent.convert_a2a_task_to_event"
1482+ with patch .object (
1483+ remote_a2a_agent ,
1484+ "convert_a2a_task_to_event" ,
1485+ autospec = True ,
14191486 ) as mock_convert :
14201487 mock_convert .return_value = mock_event
14211488
0 commit comments