Skip to content

fix(server): use keyset cursors for ListTasks page tokens - #1282

Merged
mykytanetipa merged 3 commits into
a2aproject:mainfrom
mykytanetipa:fix/1280-listtasks-keyset-cursor
Sep 30, 2026
Merged

mykytanetipa merged 3 commits into
a2aproject:mainfrom
mykytanetipa:fix/1280-listtasks-keyset-cursor

Conversation

@mykytanetipa

Copy link
Copy Markdown
Collaborator

A ListTasks page token was the ID of the first task of the next page, and each store looked that task up again to decide where to resume. If the task was updated between requests, the listing restarted and returned duplicates; if it was deleted, the request failed with InvalidParamsError.

The token now carries the sort position (timestamp, id) of the last task returned, and the next page starts strictly after it, so changes to that task no longer affect pagination. The database store no longer needs an extra query to resolve the token. Tokens issued by earlier versions are still accepted and resolved the old way.

Fixes #1280

A ListTasks page token was the ID of the first task of the next page, and
each store looked that task up again to decide where to resume. If the task
was updated between requests, the listing restarted and returned duplicates;
if it was deleted, the request failed with InvalidParamsError.

The token now carries the sort position (timestamp, id) of the last task
returned, and the next page starts strictly after it, so changes to that
task no longer affect pagination. The database store no longer needs an
extra query to resolve the token. Tokens issued by earlier versions are
still accepted and resolved the old way.

Fixes a2aproject#1280
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/tasks/database_task_store.py 92.45% 92.47% 🟢 +0.02%
src/a2a/utils/task.py 95.16% 96.55% 🟢 +1.39%
Total 92.92% 92.94% 🟢 +0.02%

Generated by coverage-comment.yml

Drop the version field and the length cap from cursor tokens: a cursor is
recognized by its exact {ts, id} shape, and the cap could reject tokens
issued for long task IDs. Convert last_updated with protobuf Timestamp
instead of hand-rolled epoch arithmetic.
@mykytanetipa
mykytanetipa marked this pull request as ready for review September 30, 2026 10:16
@mykytanetipa
mykytanetipa requested a review from a team as a code owner September 30, 2026 10:16

@sokoliva sokoliva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGMT

@mykytanetipa
mykytanetipa merged commit b7cba7c into a2aproject:main Sep 30, 2026
18 checks passed
mykytanetipa pushed a commit that referenced this pull request Sep 30, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.2.1](v1.2.0...v1.2.1)
(2026-09-30)


### Bug Fixes

* **server:** use keyset cursors for ListTasks page tokens
([#1282](#1282))
([b7cba7c](b7cba7c)),
closes [#1280](#1280)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

[Bug]: ListTasks pagination restarts or fails when the task behind the page token changes

2 participants