Skip to content

cold-store-reaper: s3_list_objects loops forever on a truncated page with a missing, empty, or repeated continuation token #1632

Closed

Description

@dezren39

Summary

ColdStore.s3_list_objects (pkgs/cold-store-reaper/cold_store.py:1555-1569) paginates ListObjectsV2 with no progress guard: it loops while True, extending the result list, and returns only when a page says IsTruncated is false. If a page claims truncation but carries no usable continuation token — or repeats the token just used — the loop re-requests the same page forever, appending duplicate objects and growing memory without bound. There is no error, no page cap, and no token-progress check.

Evidence (verified this run, main ed8e5910)

  • s3_list_objects (cold_store.py:1559-1569): token = None; each iteration builds query = {"list-type": "2"} and adds continuation-token only if token: (:1561-1563 — an empty-string token is dropped too), calls _s3_request, extends out, and returns only if not truncated.
  • _parse_s3_list_page (cold_store.py:138-172) returns token = None when the NextContinuationToken element is absent (:149, :154-155) and child.text verbatim otherwise — it never cross-checks the token against truncation.
  • Probe on the laptop driving the real module (cold_store.py sha256 d3fe48c5 identical in both clones; _s3_request stubbed to serve canned ListObjectsV2 pages; probe + log in the fleet's hidden_files/probe-2026-10-28-laneB-s3list/):
    • CONTROL — page 1 truncated with token tok2, page 2 not truncated: terminates, 2 calls, 3 objects.
    • BUG-A — every page IsTruncated=true, no NextContinuationToken element: still looping past the probe's 12-call cap (the first page is re-requested with no token each time).
    • BUG-B — every page truncated and repeats the same token tokX: still looping past the cap (each iteration re-requests the identical page and appends its objects again).
    • BUG-C — truncated with an empty token element: still looping past the cap (if token: drops it, same as BUG-A).
    • Probe run twice, identical results.
  • Existing tests cover only well-behaved pagination: the FakeS3 server in tests/test_cold_store.py always emits a fresh NextContinuationToken when truncated (:760-785), and test_list_pagination (:1318-1332) asserts the 2/2/1 happy path. No test serves a truncated page with a missing, empty, or repeated token.
  • Callers inherit the hang: backup_supabase (cold_store.py:1775) and the eviction scan (:1832) both start from s3_list_objects, so one malformed listing page wedges the nightly backup / evict-scan with no diagnostic — the same class of silent first-request death this package has been hardening against (cf. the transport-retry and curl-fallback work in cold-store-reaper: curl fallback for GET-class S3 requests (LIST) after urllib exhaustion #1625).

Real S3/R2 normally sends a token with IsTruncated=true, but this client already treats the endpoint as untrusted at every other layer (byte-count verification, transport retries, torn-body handling); a non-conforming, proxied, or partially corrupted listing response should be a loud ColdStoreError, not an infinite loop with unbounded memory growth in a long-lived nightly job.

Why it matters

backup-supabase and evict-scan are unattended nightly paths. A single bad listing page turns them into a silent infinite loop: the job never completes, never errors, and accumulates duplicate object entries in memory until it is killed — indistinguishable from a slow run until someone notices the backup is stale.

Acceptance criteria

  • s3_list_objects raises ColdStoreError (naming the bucket) when a truncated page arrives with a missing or empty NextContinuationToken.
  • s3_list_objects raises ColdStoreError when the returned continuation token does not advance (equals the token used for the current request), instead of re-requesting the same page.
  • Well-behaved multi-page listings behave exactly as today (existing test_list_pagination and the S3 transport-retry tests pass unchanged).
  • New tests cover the missing-token, empty-token, and repeated-token pages and assert the call count stays bounded (the listing aborts on the offending page, it does not keep fetching).

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    on Oct 28, 2026
  2. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  3. added
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    app/cold-store-reaperPackage/app: cold-store-reaper (pkgs/ or apps/ in this repo)
    on Oct 28, 2026
  4. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator-code-b (Lane B issue creator)
    • Session / ID: cron new-issue-creator-code-b, run 2026-10-28 04:20 CDT
    • Found via: filed this run
    • Task: Filed after verifying on main ed8e591 with a laptop probe driving the real cold_store.py (stubbed _s3_request serving canned ListObjectsV2 pages): a truncated page with a missing, empty, or repeated continuation token makes s3_list_objects loop forever appending duplicates; control two-page listing terminates correctly.
    • Expectation: effort/s — triage pins the exact guard shape (error on non-advancing token), then a small fix plus tests for the three malformed-page shapes.
    • Links: packet packet-1632.json (repo-health-code-pilot, triage)
  5. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Oct 31, 2026
  6. 1 remaining item

  7. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triaged — CONFIRMED

    • Agent: queue worker (repo-worker-code), lane A shift
    • Session / ID: queue task 1762 (triage)
    • Found via: queue claim
    • Task: triage the s3_list_objects pagination hang
    • Verdict: CONFIRMED, still live on current main b4136991. Fresh read of pkgs/cold-store-reaper/cold_store.py:1563: s3_list_objects loops while True and returns only when a page reports not-truncated; the continuation token is sent only if token:, so a truncated page with a missing/empty token re-requests page 1 and a repeated token re-requests the same page — forever, appending duplicates. No progress guard exists. The packet's probe evidence (main ed8e5910, run twice: control terminates in 2 calls; missing/empty/repeated-token pages all loop past the 12-call cap) stands unrebutted, and no open PR addresses this loop (issue cold-store-reaper: S3 list-page parser trusts the body — non-XML/wrong-root pages and bad Size values crash raw or read as an empty bucket #2733 is the separate list-page parser-trust defect).
    • Next: implementation enqueued as queue task 4065 (small_fix, same acceptance criteria as this issue: ColdStoreError naming the bucket on missing/empty/non-advancing token + FakeS3 regression tests for the three page shapes).
    • Links: follow-up queue task 4065
  8. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: queue worker (repo-worker-code), lane A shift
    • Session / ID: queue task 1762 (triage) — completed as triaged
    • Outcome: defect CONFIRMED on main b4136991; implementation follow-up enqueued as queue task 4065. This issue stays open for the fix.
    • Links: queue task 4065
  9. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

    • Agent: queue worker (repo-worker-code), lane F shift
    • Session / ID: queue task 4065
    • Found via: queue claim
    • Task: Implement the pagination progress guard in ColdStore.s3_list_objects (pkgs/cold-store-reaper): a truncated page with a missing/empty or non-advancing NextContinuationToken raises ColdStoreError naming the bucket; FakeS3 regression tests for the three page shapes
    • Expectation: effort/s — done = branch + PR with guard + tests, CI green, merged; existing pagination/transport tests unchanged
    • Links: issue cold-store-reaper: s3_list_objects loops forever on a truncated page with a missing, empty, or repeated continuation token #1632 · triage task 1762 (CONFIRMED 2026-10-30)
  10. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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

Metadata

Metadata

Assignees

Labels

app/cold-store-reaperPackage/app: cold-store-reaper (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions