fix: Stop the paginator when a page cursor repeats - #641
Merged
Conversation
razor-x
changed the base branch from
main
to
claude/python-sdk-audit-o2iid9-07-paginator-hooks
August 28, 2026 00:15
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 19:30
88e0597 to
200d836
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 20:36
200d836 to
eee59af
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 20:43
eee59af to
03dbf13
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 21:04
03dbf13 to
89281e3
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 21:09
89281e3 to
93db652
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 21:12
93db652 to
28f700c
Compare
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 21:17
28f700c to
d1d8c62
Compare
Base automatically changed from
claude/python-sdk-audit-o2iid9-07-paginator-hooks
to
main
August 28, 2026 21:23
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y1RzepycXEYA3LStfjt8cY
razor-x
force-pushed
the
claude/python-sdk-audit-o2iid9-08-cursor-guard
branch
from
August 28, 2026 21:29
d1d8c62 to
c5f8974
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Neither paginator had any loop guard: a server regression or proxy-cached page that repeats a
next_page_cursorloopedflatten()forever and grewflatten_to_list()until OOM — the per-request timeout resets every page, so nothing else stops the process (SDK audit finding M6; ports JS #1006 / PHP #466).flatten/flatten_to_list× sync / async), is centralized in one private_walk()generator per paginator — the same single-walker shape as the JS fix, so the guard cannot be missing from one of the copies._walk()keeps a seen-cursor set and stops silently when the server hands back a cursor it has already followed.Stacked on #640; diff shrinks as the stack merges.
Testing
New tests (sync
flatten_to_list, syncflatten, async) against a recording server that pins one cursor: the paginator fetches the first page, follows the cursor once, sees it repeat, and stops — request count pinned at exactly 2, per the JS-wave lesson that loop-guard tests must count requests, not just observe termination.Revert check: with the previous paginator restored, the test loops until killed by a 15s timeout — the audit's infinite-loop symptom.
Full suite: 235 passed; mypy, pylint (10.00), black clean.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Y1RzepycXEYA3LStfjt8cY
Generated by Claude Code