[Bug]: Framework-written FAILED status drops status.timestamp, persisting last_updated as NULL
Les mainteneurs répondent en général sous 3 jours
Évaluation
Cette issue n'a pas encore été évaluée.
Description
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:
ListTasksordering (which sorts onlast_updated) drops the task to the end;- the
status_timestamp_afterfilter never matches the task again; - API responses lose the
status.timestampfield 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 previousstatus.timestampwhen 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
- Langage dominant
- Python
- Étoiles
- 2.2k
- Forks
- 518
- Merge moyen
- 3 j 19 h
- PR mergées (30 j)
- 43
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de a2aproject/a2a-python
-
[Bug]: `A2A-Version` validation ignores minor versions and the query parameterPeut-être pris @rohityan l’a pris il y a 59 jours. Ouvertecomponent: server status:awaiting response
Difficulté 2/5 1-3 heures Accessibilité débutants 75/100
a2aproject/a2a-python#1204 · 3 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 3 jours
-
[Bug]: REST task/request id sanitizationPeut-être pris @Linux2010 l’a pris il y a 117 jours. Ouvertemaintainers-only
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
a2aproject/a2a-python#805 · 1 commentaire ·
Les mainteneurs répondent en général sous 3 jours
-
Zero-downtime migration mode stores tasks without last_updatedPeut-être pris @ConnorMoss02 l’a pris aujourd’hui. Ouvertecomponent: server status:awaiting response
a2aproject/a2a-python#1371 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 3 jours
-
[Bug]: v0.3 JSON-RPC compat adapter returns -32603 for all A2A errors (e.g. TaskNotFound should be -32001)Peut-être pris @shwetangsinha0509 l’a pris il y a 1 jour. Ouvertecomponent: server status:awaiting response
a2aproject/a2a-python#1368 · 2 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 3 jours
-
[Bug]: VersionedDatabaseTaskStore get() and delete() fail with "no such table" when initialize() was not calledPeut-être pris @cchinchilla-dev l’a pris il y a 1 jour. Ouvertecomponent: server status:awaiting response
a2aproject/a2a-python#1356 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 3 jours
Toutes les issues de a2aproject/a2a-python
Issues similaires
-
[Resource]: LoreOuverteresource-submission validation-passed
Difficulté 1/5 Moins d'une heure Accessibilité débutants 88/100
hesreallyhim/awesome-claude-code#3234 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
openminion/openminion#376 ·
Les mainteneurs répondent en général sous 1 jour
-
Type: ☹︎ Bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
posit-dev/great-tables#1028 ·
Les mainteneurs répondent en général sous 1 jour
-
integration: apple_tv
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
home-assistant/core#188282 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
A "since" range is worded as a career: "the past two seasons" says "regular season career (2025 on)"OuverteP4: low
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
jeffknupp/association#538 ·