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
14 changes: 8 additions & 6 deletions src/a2a/server/tasks/task_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ def append_artifact_to_task(task: Task, event: TaskArtifactUpdateEvent) -> None:
dict(new_artifact_data.metadata.items())
)
else:
# We received a chunk to append, but we don't have an existing artifact.
# we will ignore this chunk
logger.warning(
'Received append=True for nonexistent artifact index %s in task %s. Ignoring chunk.',
artifact_id,
task.id,
# We received a chunk to append, but there is no existing artifact
# with this id. Silently dropping the chunk would hide a real bug
# in the caller (e.g. generating a fresh artifact_id on every
# add_artifact call instead of pinning one), so we raise.
raise ValueError(
f'append=True but no artifact with id {artifact_id!r} exists on '
f'task {task.id!r}. Create the artifact first (append=False) '
f'before appending to it.'
)
Comment on lines +81 to 85
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider using InvalidParamsError instead of the built-in ValueError. Since append_artifact_to_task is part of the task event processing pipeline, using the project's custom A2AError subclasses ensures that the error is correctly caught and mapped to a structured protocol response. Note that per repository rules, exception classes must be raised directly without instantiation.

        raise InvalidParamsError
References
  1. Raise exception classes directly, without instantiating them (e.g., raise MyError instead of raise MyError()).



Expand Down
5 changes: 4 additions & 1 deletion tests/server/tasks/test_task_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ def test_append_artifact_to_task():
assert len(task.artifacts[1].parts) == 1

# Test appending part to a task that does not have a matching artifact
# raises ValueError instead of silently dropping the chunk (#1038)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Update the comment to reflect the change to InvalidParamsError.

Suggested change
# raises ValueError instead of silently dropping the chunk (#1038)
# raises InvalidParamsError instead of silently dropping the chunk (#1038)

non_existing_artifact_with_parts = Artifact(
artifact_id='artifact-456', parts=[Part(text='Part 1')]
)
Expand All @@ -438,7 +439,9 @@ def test_append_artifact_to_task():
task_id='123',
context_id='123',
)
append_artifact_to_task(task, append_event_5)
with pytest.raises(ValueError, match='append=True but no artifact with id'):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Update the test to assert InvalidParamsError instead of ValueError to align with the implementation change. Note that the exception is raised without a message per repository rules.

Suggested change
with pytest.raises(ValueError, match='append=True but no artifact with id'):
with pytest.raises(InvalidParamsError):
References
  1. Raise exception classes directly, without instantiating them (e.g., raise MyError instead of raise MyError()).

append_artifact_to_task(task, append_event_5)
# Artifacts unchanged — the bad chunk was not silently added
assert len(task.artifacts) == 2
assert len(task.artifacts[0].parts) == 2
assert len(task.artifacts[1].parts) == 1
Loading