What happened?
In both DatabaseTaskStore.list and InMemoryTaskStore.list, next_page_token is the ID of the first task of the next page, not a position in the sort order. The next request looks that task up again and continues from wherever it is now:
So whenever that one task changes between two page requests, pagination breaks:
- The task is updated. It moves to the top of the order, so the next page starts over from the top and the client gets duplicates.
- The task is deleted. The lookup finds nothing and the request fails with
InvalidParamsError: Invalid page token, so the client can't continue.
Expected: the token marks a position in the sort order, so changing or deleting one task doesn't send the listing back to the start or invalidate it.
Possible fix: put the last_updated and id of the last task returned in the token, and start the next page right after that task: last_updated < token.last_updated OR (last_updated = token.last_updated AND id < token.id). A task updated while the client is paging can still move above the cursor and be missed in that pass, but the listing never goes backwards or fails.
Present on main (0d5473c) and in v1.1.5.
Relevant log output
Code of Conduct
What happened?
In both
DatabaseTaskStore.listandInMemoryTaskStore.list,next_page_tokenis the ID of the first task of the next page, not a position in the sort order. The next request looks that task up again and continues from wherever it is now:DatabaseTaskStore(database_task_store.py#L261-L309) loads the task by ID and filters on its currentlast_updated.InMemoryTaskStore(inmemory_task_store.py#L124-L146) finds the task's index in the freshly filtered and sorted list.So whenever that one task changes between two page requests, pagination breaks:
InvalidParamsError: Invalid page token, so the client can't continue.Expected: the token marks a position in the sort order, so changing or deleting one task doesn't send the listing back to the start or invalidate it.
Possible fix: put the
last_updatedandidof the last task returned in the token, and start the next page right after that task:last_updated < token.last_updated OR (last_updated = token.last_updated AND id < token.id). A task updated while the client is paging can still move above the cursor and be missed in that pass, but the listing never goes backwards or fails.Present on
main(0d5473c) and in v1.1.5.Relevant log output
Code of Conduct