Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/a2a/server/agent_execution/active_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,11 @@ async def _mark_task_as_failed(self, exception: Exception) -> Task | None:
pass
return task

@property
def task_created(self) -> bool:
"""Whether a task exists, so ``get_task()`` will not block."""
return self._task_created.is_set()

async def get_task(self) -> Task:
"""Get task from db."""
await self._task_created.wait()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
from a2a.utils.errors import (
ExtendedAgentCardNotConfiguredError,
InternalError,
InvalidAgentResponseError,
InvalidParamsError,
PushNotificationNotSupportedError,
TaskNotCancelableError,
Expand Down Expand Up @@ -444,6 +445,11 @@ async def on_message_send( # noqa: D102
# pushed after a Message.

if result is None:
if not active_task.task_created:
# A silent executor means get_task() would block forever.
raise InvalidAgentResponseError(
message='Agent execution produced no events'
)
logger.debug('Missing result for task %s', request_context.task_id)
result = await active_task.get_task()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2776,3 +2776,38 @@ async def test_on_message_send_stream_message_response_releases_active_task() ->
assert [type(event) for event in events] == [Message]
assert await _wait_until_released(handler, baseline) == set()
assert handler._active_task_registry._active_tasks == {}


class SilentExecutor(AgentExecutor):
"""Returns normally without producing any event (issue #1330)."""

async def execute(self, context: RequestContext, event_queue: EventQueue):
return None

async def cancel(self, context: RequestContext, event_queue: EventQueue):
pass


@pytest.mark.asyncio
async def test_on_message_send_raises_when_agent_produces_no_events():
"""A silent executor must fail fast instead of hanging forever (#1330)."""
request_handler = DefaultRequestHandlerV2(
agent_executor=SilentExecutor(),
task_store=InMemoryTaskStore(),
agent_card=create_default_agent_card(),
)
params = SendMessageRequest(
message=Message(
role=Role.ROLE_USER,
message_id='msg_silent',
parts=[Part(text='Hi')],
),
configuration=SendMessageConfiguration(
accepted_output_modes=['text/plain']
),
)

with pytest.raises(InvalidAgentResponseError, match='no events'):
await request_handler.on_message_send(
params, create_server_call_context()
)
Loading