fix: harden pagination and exit-code classification - #3
Conversation
Three fixes, each validated by differential testing during the TypeScript port audit (PR #2) and backported here: - List() no longer reports a nextPageToken when --limit discarded items from the final fetched page. The server's token points past the discarded tail, so resuming from it silently skipped data. A limit landing exactly on a page boundary still keeps the token. - The --all pagination loop is now bounded. Previously it terminated only on an empty nextPageToken, trusting the server completely; a server repeating a token looped forever, accumulating pages in memory without limit. Two guards: a repeated token stops the loop (it can only re-fetch the same page), and a 10,000-request ceiling returns an error. Neither is reachable on a well-behaved server. - exitCode() now applies the same separator-stripping normalization to the quota/rate-limit reason test that the auth test already used, so RATE_LIMIT_EXCEEDED and QUOTA_EXCEEDED (the SCREAMING_SNAKE style newer Google API surfaces return) exit 5 like their camelCase equivalents, instead of falling through to 6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesPagination behavior
Exit code classification
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
A server echoing the caller's --page-token back as nextPageToken is the same loop as any other repeated token, but seenTokens started empty, so the page was fetched and appended twice before detection fired. Seed the set with options.PageToken and cover it with a regression test asserting one request, no duplicated items, and no resume token. Found by CodeRabbit on PR #3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| // again. Stop and report "", which correctly says "no valid resume | ||
| // point" rather than handing back a token that loops. | ||
| if _, seen := seenTokens[result.NextPageToken]; seen { |
There was a problem hiding this comment.
Request ceiling rejects valid pagination
When --all uses --page-size 1 for more than 10,000 results, this ceiling rejects a traversal whose tokens are still advancing, causing the command to discard all accumulated output and return an error.
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/youtube/list.go
Line: 82-84
Comment:
**Request ceiling rejects valid pagination**
When `--all` uses `--page-size 1` for more than 10,000 results, this ceiling rejects a traversal whose tokens are still advancing, causing the command to discard all accumulated output and return an error.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Intentionally skipping this one. Reaching the ceiling requires 10,000 requests, and every paginated request costs ≥1 unit of the default 10,000-unit daily quota — so the API's own quotaExceeded cuts the traversal off at or before the point the ceiling fires (and a mid-pagination quota error discards accumulated output the same way; that's the pre-existing behavior for any mid-traversal failure). Endpoints with page sizes above 100 (live chat, up to 2000) reject --all by design, so the 1-unit endpoints are the only ones that paginate. A bound has to exist somewhere to stop a non-terminating server — a legitimate 10,001st token is indistinguishable from a hostile one — and --limit is the documented escape hatch (the error message points to it). The value also stays in parity with the TypeScript port's pinned MAX_PAGES = 10000 (#2, D3).
There was a problem hiding this comment.
That's a sound argument — the API quota acts as a natural ceiling below the guard for 1-unit endpoints, high-page-size endpoints reject --all by design, and the TypeScript port parity is a reasonable anchor. Understood, withdrawing the concern.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
|
Bot-review triage summary — all bots green on HEAD (92114c2): Fixed (1):
Skipped (1, with rationale on the thread):
CI ×3, Macroscope, CodeRabbit, Devin, Greptile: all pass. |
Backports the three behavioral bug fixes that were found and validated by differential testing during the TypeScript port audit (#2). Each was confirmed present in the Go source before fixing.
Fixes
1. No misleading resume token on
--limittruncation (internal/youtube/list.go)List()truncated the final page to honor--limitbut still reported the server'snextPageToken— which points past the discarded items, so resuming from it silently skipped data. A truncated page now reports""; a limit landing exactly on a page boundary still keeps the token. The old behavior was locked in byTestListPaginationLimitAndToken, which is updated, and the boundary case gets a new test.2.
--allpagination is bounded (internal/youtube/list.go)The loop terminated only on an empty
nextPageToken, trusting the server completely — a server repeating a token (bug, buggy proxy, or hostile endpoint) looped forever, accumulating every page in memory. During the port audit a test harness returning a constant token consumed ~59 GB of RSS before being killed. Two guards now apply:--limitNeither is reachable on a well-behaved server: at the largest page size any endpoint accepts (2000, live chat) the ceiling allows 20M items, and every other endpoint caps at 50 or 100 per page.
3. Consistent error-reason normalization in
exitCode()(cmd/oytc/main.go)The auth test stripped
_/-from reasons before matching, but the quota/rate-limit test matched against the raw string — souserRateLimitExceededcorrectly exited 5 whileRATE_LIMIT_EXCEEDEDfell through to 6. Google returns SCREAMING_SNAKE reasons on newer API surfaces, so the miss was real. One normalization now applies to every test in the table. New table cases coverQUOTA_EXCEEDED,RATE_LIMIT_EXCEEDED,rate-limit-exceeded, anduserRateLimitExceeded.Docs
docs/commands.mdpagination section now documents the truncation/resume-token rule.Release
Versioning here is tag-driven (no version file;
internal/version.Versionis injected via ldflags from the tag). After merge, cut the release with:Verification
gofmtclean,go vetclean,go test -race ./...green🤖 Generated with Claude Code
Note
Harden pagination loop termination and exit-code classification for API error reasons
Client.Listnow clearsNextPageTokenwhen a page is truncated by--limit, preserves it on exact page boundaries, and stops if the server echoes a previously seen token.MaxListRequests = 10000paginated requests; exceeding it returns an error suggesting--limit.exitCodenormalizes API error reason strings by lowercasing and stripping_and-before classifying quota/rate-limit (exit 5) and key/permission (exit 3) errors, handling variants likeRATE_LIMIT_EXCEEDEDorrate-limit-exceeded.nextPageToken, preventing resumption past discarded items.Macroscope summarized 92114c2.
Greptile Summary
This PR hardens pagination and normalizes API error-reason classification.
Confidence Score: 3/5
The request limit should be fixed before merging because it can reject a valid
--all --page-size 1traversal and discard all fetched output.List commands permit one item per request, so a valid result set exceeding 10,000 items reaches the new limit despite continuously advancing tokens; callers then return the error without rendering the accumulated result.
Files Needing Attention: internal/youtube/list.go
What T-Rex did
Important Files Changed
Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "fix: seed pagination loop detection with..." | Re-trigger Greptile