Skip to content

Keep A2A canceled sticky against message/send persist - #331

Merged
tylerreckart merged 2 commits into
mainfrom
fix/a2a-send-preserve-cancel
Sep 21, 2026
Merged

tylerreckart merged 2 commits into
mainfrom
fix/a2a-send-preserve-cancel

Conversation

@cursor

@cursor cursor Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

tasks/cancel persists canceled while an in-flight message/send is still unwinding. The stream path already refuses to overwrite that row (get_a2a_task then skip the terminal persist). The unary send path always wrote build_terminal_task's completed/failed through update_a2a_task, so a successful (or failed) send could revive a canceled task. tasks/get then disagreed with the cancel RPC, and the send client received a completed Task.

Exception / tenant-disabled paths on send had the same hole: they wrote failed without checking for a racing cancel.

Fix

update_a2a_task keeps canceled sticky: a non-canceled state is a no-op when the row is already canceled. Re-writing canceled (the cancel handler itself) still lands so updated_at / error stay current. Unary message/send treats that CAS miss as request cancelled instead of returning a completed Task.

Tests

unit_request_log — canceled is not overwritten by completed/failed; re-writing canceled still succeeds; working → completed still works when cancel never landed.

Independently mergeable against main. Code hunks are disjoint from #322/#330 (list_conversations / EntryFilter) and from #321–#329. CHANGELOG [Unreleased] will need a rebase after whichever sibling merges first.

Open in Web View Automation 

Note

Medium Risk
Changes durable A2A task state and unary RPC outcomes on cancel/send races; behavior is narrower (cancel wins) but affects protocol clients that relied on a completed send response after cancel.

Overview
Fixes a race where tasks/cancel could persist canceled while unary message/send was still finishing, then update_a2a_task wrote completed / failed and the RPC returned a terminal Task that tasks/get contradicted. The stream path already avoided that overwrite; this change aligns unary send.

TenantStore::update_a2a_task now treats canceled as sticky in SQL: updates with a non-canceled state are a no-op when the row is already canceled; re-writing canceled (e.g. from tasks/cancel) still succeeds so updated_at / error text stay current. Unary message/send checks that CAS: if persist fails and the row is still canceled, it responds with request cancelled instead of a completed Task.

Adds a unit_request_log test for sticky cancel plus normal working → completed when cancel never landed, and documents the behavior in CHANGELOG and tenant_store.h.

Reviewed by Cursor Bugbot for commit 7733e34. Bugbot is set up for automated code reviews on this repo. Configure here.

tasks/cancel can land while unary message/send is still unwinding.
The stream path already refused to overwrite canceled; update_a2a_task
now enforces the same so completed/failed cannot clobber cancel, and
the send RPC returns request cancelled instead of a completed Task.

Co-authored-by: Tyler Reckart <tylerreckart@users.noreply.github.com>
@tylerreckart
tylerreckart enabled auto-merge (squash) September 21, 2026 13:23
@tylerreckart
tylerreckart merged commit 99783c7 into main Sep 21, 2026
2 of 6 checks passed
@greptile-apps greptile-apps Bot mentioned this pull request Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants