Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[Bug]: Framework-written FAILED status drops status.timestamp, persisting last_updated as NULL

Aperta
#1,331 0 commenti 0 reazioni 2 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 2 giorni

@rohityan ci sta già lavorando.

Dal 9/10/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

What happened?

When the framework itself writes a terminal FAILED state (consumer failure, producer failure), the resulting TaskStatus carries no timestamp. Because TaskManager.save_task_event replaces the status wholesale via task.status.CopyFrom(event.status) (src/a2a/server/tasks/task_manager.py:265), the previously stamped status.timestamp is cleared, and DatabaseTaskStore.save derives last_updated from status.timestamp (src/a2a/server/tasks/database_task_store.py:152-156) — persisting NULL.

Consequences:

  • ListTasks ordering (which sorts on last_updated) drops the task to the end;
  • the status_timestamp_after filter never matches the task again;
  • API responses lose the status.timestamp field entirely.

The behavior is also internally inconsistent: the cancel-failure path _mark_task_as_failed (src/a2a/server/agent_execution/active_task.py:945-963) copies the existing status and only overwrites state (preserving the timestamp), while the consumer failure path _write_failed_status (active_task.py:161-169) and the producer failure path construct a fresh TaskStatus(state=...) with no timestamp.

Reproduction
Self-contained script (main @ 494a8ec, Python 3.10)
import asyncio

from a2a.auth.user import UnauthenticatedUser
from a2a.helpers.proto_helpers import new_task_from_user_message
from a2a.server.agent_execution import AgentExecutor
from a2a.server.context import ServerCallContext
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandlerV2
from a2a.server.tasks import InMemoryTaskStore
from a2a.types.a2a_pb2 import (
    AgentCapabilities,
    AgentCard,
    Message,
    Part,
    Role,
    SendMessageConfiguration,
    SendMessageRequest,
    TaskState,
)


class ExplodingExecutor(AgentExecutor):
    task_id = None

    async def execute(self, context, event_queue):
        task = new_task_from_user_message(context.message)
        self.task_id = task.id
        await event_queue.enqueue_event(task)
        raise RuntimeError('agent crashed mid-flight')

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


async def main():
    task_store = InMemoryTaskStore()
    executor = ExplodingExecutor()
    handler = DefaultRequestHandlerV2(
        agent_executor=executor,
        task_store=task_store,
        agent_card=AgentCard(
            name='t',
            version='1.0',
            capabilities=AgentCapabilities(streaming=True),
        ),
    )
    params = SendMessageRequest(
        message=Message(
            role=Role.ROLE_USER, message_id='m1', parts=[Part(text='Hi')]
        ),
        configuration=SendMessageConfiguration(
            accepted_output_modes=['text/plain']
        ),
    )
    try:
        await handler.on_message_send(
            params, ServerCallContext(user=UnauthenticatedUser())
        )
    except Exception as e:
        print(f'on_message_send raised {type(e).__name__} (expected)')
    task = await task_store.get(
        executor.task_id, ServerCallContext(user=UnauthenticatedUser())
    )
    print(
        f'persisted: state={TaskState.Name(task.status.state)}, '
        f'timestamp set={task.status.HasField("timestamp")}'
    )


asyncio.run(main())

Observed output:

on_message_send raised RuntimeError (expected)
persisted: state=TASK_STATE_FAILED, timestamp set=False
Expected behavior

Every task state transition the framework writes — including failures — should carry a status.timestamp, matching what TaskUpdater.update_status does for agent-driven transitions.

Suggested fix

Either:

  • stamp the current time in the three framework failure paths (_write_failed_status, the producer failure path, and — for consistency — _mark_task_as_failed), or
  • in TaskManager.save_task_event, preserve the previous status.timestamp when the incoming status event has none.

Happy to submit a PR with tests.

Related

Observed as a secondary effect in the reproduction of #1313.

Code of Conduct
  • I agree to follow this project's Code of Conduct
Lingua principale
Python
Stelle
2.2k
Fork
509
Merge medio
3g 11h
PR unite (30g)
43

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di a2aproject/a2a-python

Tutte le issue di a2aproject/a2a-python

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.