fix(contacts): reject repeated dedupe page tokens - #1083
Conversation
contactsDedupeList assigned Connections.List nextPageToken with no seen-set. Default gog contacts dedupe --max 0 scans every page, so a repeated Google token hung the CLI. Route listing through collectAllPages so a repeated token returns pagination loop: repeated page token instead of looping. Signed-off-by: Sebastien Tardif <SebTardif@ncf.ca>
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs changes before merge. Reviewed September 5, 2026, 3:45 AM ET / 07:45 UTC. ClawSweeper reviewWhat this changesThe PR routes contact dedupe pagination through the existing repeated-token guard and adds a command-level regression test. Merge readiness⛔ Needs changes before merge - 2 items remain This remains necessary: current main and v0.39.0 retain the unguarded dedupe loop, while the cited merged fixes cover separate callers. The implementation and recorded fault-injection proof support keeping this PR as the landing candidate. Priority: P2 Review scores
Verification
How this fits togetherContact dedupe reads personal contacts from the Google People API and groups likely duplicates for preview or confirmed merging. Pagination must finish successfully before the command emits results or starts contact updates. flowchart TD
A[Contact dedupe command] --> B[Fetch People API page]
B --> C[Collect non-null contacts]
C --> D{Scan limit or final page?}
D -->|Yes| E[Build duplicate groups]
D -->|No| F{Page token already seen?}
F -->|No| B
F -->|Yes| G[Return error before output or updates]
E --> H[Preview or confirmed merge]
Before merge
Findings
Agent review detailsSecurityNone. Review metrics
Technical reviewBest possible solution: Dedupe should terminate malformed pagination before producing a plan or applying changes while preserving bounded scans and normal multi-page results. Do we have a high-confidence way to reproduce the issue? Yes: consecutive Connections responses carrying nextPageToken=stuck exercise the unguarded default scan on current main. Source inspection and the supplied before/after command transcript establish the mechanism; this review did not execute it. Is this the best way to solve the issue? Yes: reusing collectAllPages is the narrowest established repair, and the adapter preserves non-null contact counting and early completion at --max. Full review comments:
Overall correctness: patch is correct AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 25703c789da5. LabelsLabel changes:
Label justifications:
EvidenceAcceptance criteria:
What I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
|
|
Superseded by #1087, which incorporates contact-dedupe cycle protection while preserving early completion at --max. Repeated tokens stop before a partial plan or contact changes. Thanks @SebTardif. Superseded by #1087, which incorporates recurring-instance cycle protection while preserving immediate success when the requested occurrence is found. Failed lookup performs no event deletion. Thanks @SebTardif. Superseded by #1087, which incorporates cycle protection for Drive tree, inventory, and disk-usage scans. All three stock CLI commands were verified to fail without partial success output. Thanks @SebTardif. |
What Problem This Solves
gog contacts dedupelists personal connections withpageToken = resp.NextPageTokenand no seen-set. The public default is--max 0, which means scan every page. If Google repeatsnextPageToken, dedupe never finishes and the CLI hangs until the process is killed.Why
The repo already has this guard.
collectAllPageserrors withpagination loop: repeated page token. Calendar listing uses it after #1004. People raw email resolve uses it in #1044. The Gmail--from-contactfallback uses it in #1045. Contacts export group listing uses it in #1046. Dedupe listing lives in the samecmdpackage, so it now calls that helper instead of growing a second loop.--maxstill stops the scan early by returning an empty next token once the requested count is reached.User Impact
A stuck Connections page token now fails with
repeated page tokeninstead of hanginggog contacts dedupeon the default all-pages scan.Evidence
terminal output from the unpatched connections listing loop versus this patch. A People httptest server always returns
nextPageToken=stuckfrompeople.me.connections.list. The public command isgog contacts dedupewith default--max 0.Unpatched (the HTTP peer answers immediately; the handler returns HTTP 400 after a third list call so the loop cannot run forever):
Patched (same command, same stuck token, returns immediately):
Live CLI still exposes the default all-pages scan:
go test ./internal/cmd -count=1 -run TestContactsDedupecompleted on this tree.gofmtis clean.golangci-lint run ./internal/cmdreported 0 issues.Real behavior proof
nextPageTokenvalues could hanggog contacts dedupewhile listingpeople.me.connectionson the default--max 0scan.C:\Users\sebta\.grok\tmp\pr-gate-batch\gogcli-f020onfix/contacts-dedupe-page-tokenfromorigin/mainat25703c78.go test ./internal/cmd -run TestContactsDedupeExecuteRejectsRepeatedPageToken -count=1 -timeout 60s -vagainst a People httptest that always returnsnextPageToken=stuckfrompeople.me.connections.list, then ranbin/gog contacts dedupe --helpon the binary built from this branch.pagination loop: repeated page token "stuck"after 2 list calls (0.07s). The unpatched loop made 3 list calls and only stopped when the handler returned HTTP 400.contactsDedupeListreturns the repeated-token error on the next page instead of hanging.gog contacts dedupe --helpstill shows--max=0as scan all pages.people.connections.listresponse that actually repeats a token. That requires a faulty Google page, which we cannot force from a healthy account.Related
collectAllPagesininternal/cmd/paging.go, by calendar listing in #1004, by People raw email resolve in #1044, by the Gmail--from-contactfallback in #1045, and by contacts export group listing in #1046.62a7257ab(feat(contacts): add dedupe preview, #555, 2026-05-05, 123 days ago).