Skip to content

fix(paging): stop calendar and Gmail listing on a repeated page token - #1004

Open
SebTardif wants to merge 1 commit into
openclaw:mainfrom
SebTardif:fix/repeated-page-tokens
Open

fix(paging): stop calendar and Gmail listing on a repeated page token#1004
SebTardif wants to merge 1 commit into
openclaw:mainfrom
SebTardif:fix/repeated-page-tokens

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

What Problem This Solves

gog calendar events --all (and any other path that lists every calendar) walks CalendarList.List with no memory of page tokens it has already seen. Gmail backup ID listing does the same on users.messages.list. If Google repeats nextPageToken, both loops never finish.

Why

The repo already has this guard. collectAllPages errors with pagination loop: repeated page token. Photos Picker and Drive changes do the same with a local seen-token map. listCalendarList is in package cmd, so it now calls collectAllPages instead of growing a second helper. Gmail backup lives under internal/backup/gmail and cannot import cmd, so it adds the same local seenTokens map Photos Picker already uses.

User Impact

A stuck Google page token now fails with repeated page token instead of hanging gog calendar events --all or a Gmail backup list until the process is killed.

Evidence

terminal output from the unpatched loops versus this patch, using a Calendar httptest server and a Gmail source that always return nextPageToken=stuck.

Unpatched (300ms context; the HTTP peer answers immediately, so the deadline is the only stop):

$ go test ./internal/cmd/ -count=1 -timeout 5s -v -run TestListCalendarListRejectsRepeatedPageToken
=== RUN   TestListCalendarListRejectsRepeatedPageToken
    calendar_list_test.go:115: err = context deadline exceeded
--- FAIL: TestListCalendarListRejectsRepeatedPageToken (0.30s)
FAIL

$ go test ./internal/backup/gmail/ -count=1 -timeout 5s -v -run TestListMessageIDsRejectsRepeatedPageToken
=== RUN   TestListMessageIDsRejectsRepeatedPageToken
    fetch_test.go:146: err = list Gmail backup messages: context deadline exceeded
--- FAIL: TestListMessageIDsRejectsRepeatedPageToken (0.30s)
FAIL

Patched (same commands, same stuck token, returns immediately):

$ go test ./internal/cmd/ -count=1 -timeout 10s -v -run TestListCalendarListRejectsRepeatedPageToken
=== RUN   TestListCalendarListRejectsRepeatedPageToken
    calendar_list_test.go:117: err = pagination loop: repeated page token "stuck"
--- PASS: TestListCalendarListRejectsRepeatedPageToken (0.00s)
PASS

$ go test ./internal/backup/gmail/ -count=1 -timeout 10s -v -run TestListMessageIDsRejectsRepeatedPageToken
=== RUN   TestListMessageIDsRejectsRepeatedPageToken
    fetch_test.go:148: err = list Gmail backup messages: repeated page token "stuck"
--- PASS: TestListMessageIDsRejectsRepeatedPageToken (0.00s)
PASS

$ go run proof_gmail_pages.go
elapsed=0s ids=[] err=list Gmail backup messages: repeated page token "stuck"

go test ./internal/cmd/ ./internal/backup/gmail/ -count=1 also completed on this tree.

Real behavior proof

  • Behavior or issue addressed: Repeated Google nextPageToken values could hang calendar listing (--all) and Gmail backup ID listing.
  • Real environment tested: macOS Darwin 25.6.0 arm64, Go go1.26.6, full clone at /tmp/pr-gogcli-pages on fix/repeated-page-tokens from upstream/main.
  • Exact steps or command run after this patch: From /tmp/pr-gogcli-pages, ran the same go test commands shown above against a Calendar httptest that always returns nextPageToken=stuck, then ran go run proof_gmail_pages.go which calls exported ListMessageIDs with a source that always returns NextPageToken "stuck".
  • Evidence after fix: terminal output above. Calendar listing now returns pagination loop: repeated page token "stuck" in 0.00s. Gmail ListMessageIDs returns list Gmail backup messages: repeated page token "stuck" with elapsed=0s.
  • Observed result after fix: The stuck token no longer runs until a context deadline. The process returns the repeated-token error on the next page instead of hanging.
  • What was not tested: A live Google CalendarList or Gmail messages.list response that actually repeats a token. That requires a faulty Google page, which we cannot force from a healthy account.

Related

  • Same-repo guard already used by collectAllPages in internal/cmd/paging.go, Photos Picker (errPhotosPickerRepeatedPage), and Drive changes.
  • Calendar listing without a seen-set dates to #131 (9977c0be, 2026-02-15, 184 days ago).
  • Gmail backup ID paging dates to 12461f5d (2026-06-13, 66 days ago).

listCalendarList now uses collectAllPages. Gmail backup ID listing
rejects a repeated NextPageToken the same way Photos Picker already
does, so a stuck Google token cannot hang --all or backup.

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@clawsweeper

clawsweeper Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Aug 18, 2026
@clawsweeper

clawsweeper Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed August 18, 2026, 7:22 PM ET / 23:22 UTC.

ClawSweeper review

What this changes

The PR detects repeated Google page tokens in calendar-list and Gmail-backup ID pagination and adds regression coverage for both paths.

Regression provenance

Possible regression — probable (reviewed change; failure trace). No predecessor PR is attributed.

Merge readiness

Ready for maintainer review

Keep open: current main lacks both repeated-token guards, and this focused patch follows the repository’s existing pagination contract with credible terminal proof.

Priority: P2
Reviewed head: f68fe084483d7d3ece9efc01e62e73ee2772cc5c

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused implementation, matching regression coverage, and direct terminal evidence make this a normal good-quality fix.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.
Evidence reviewed 6 items Current calendar behavior: Current main’s calendar-list loop advances until only an empty next-page token, so a repeated non-empty token has no termination condition.
Current Gmail behavior: Current main’s backup ID loop similarly advances from every non-empty next-page token without recording previously returned tokens.
Existing pagination contract: The shared command helper already records page tokens and returns a quoted repeated-token error, matching the calendar implementation approach.
Findings None None.
Security None None.

Live Verification

Command: go test ./internal/cmd ./internal/backup/gmail -count=1 -run 'Test(ListCalendarList|ListMessageIDs)RejectsRepeatedPageToken' -v

Result: FAIL (failed) — execution before step 1 run: sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz

sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz

Assertions:

  • FAIL expect_output: repeated page token "stuck"
  • FAIL expect_output: PASS

How this fits together

Calendar commands enumerate calendars before selecting or fetching events, while Gmail backup enumerates message IDs before fetching content. Both consume Google API page tokens; this change turns a token cycle into an explicit error rather than an unbounded request loop.

flowchart LR
  A[Calendar or Gmail command] --> B[Google list request]
  B --> C[Items and next token]
  C --> D{Token seen before?}
  D -->|No| B
  D -->|Yes| E[Return paging error]
  C --> F[Event selection or backup fetch]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test delta production +12/-15; tests +62 across 4 files The production change is a small loop replacement plus a local guard, with most added lines providing focused regression coverage.

Technical review

Best possible solution:

Land the focused guards after normal checks complete so both affected list flows fail explicitly instead of consuming requests indefinitely.

Do we have a high-confidence way to reproduce the issue?

Yes. Deterministic Calendar HTTP and Gmail source harnesses can return the same non-empty token repeatedly, and the source directly establishes the current-main loop condition.

Is this the best way to solve the issue?

Yes. Reusing the existing shared calendar collector and adding an equivalent local guard in the isolated Gmail package is the narrowest maintainable solution.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against eb85a99366eb.

Labels

Label changes:

  • add P2: A repeated remote page token can stall calendar and Gmail backup workflows, but the bounded patch and existing guard pattern make this normal-priority repair work.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.

Label justifications:

  • P2: A repeated remote page token can stall calendar and Gmail backup workflows, but the bounded patch and existing guard pattern make this normal-priority repair work.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix terminal output from deterministic Calendar and Gmail fault harnesses, showing immediate repeated-token errors without sensitive values.

Evidence

What I checked:

Likely related people:

  • Peter Steinberger: Introduced the extracted Gmail backup fetch engine and is attributed by current-main blame for the shared paging helper. (role: Gmail backup engine author and recent pagination-area contributor; confidence: high; commits: 12461f5df6f5, 45b5d766e137; files: internal/backup/gmail/fetch.go, internal/cmd/paging.go)
  • salmonumbrella: The related merged calendar-selection change introduced the calendar-list path implicated by this repair. (role: calendar selection contributor; confidence: medium; commits: 9977c0bedbc5; files: internal/cmd/calendar_list.go)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant