Skip to content

cloudflare-export: R2 bucket enumeration fails open — a 500/malformed buckets response publishes r2Buckets: [] in a status:ok document, and pagination past 100 buckets is silently dropped #2574

Closed

Description

@dezren39

cloudflare-export: R2 bucket enumeration fails open — a 500/malformed buckets response publishes r2Buckets: [] in a status:ok document, and pagination past 100 buckets is silently dropped

Evidence (verified this run, main af19d7fd)

pm-stack/cloudflare-export/cloudflare-quota-export.mjs, in run():

  • :393 — the bucket enumeration is a single request: `/accounts/${acct}/r2/buckets?per_page=100`. No further page is ever requested and result_info is never read.
  • :394 — only 401/403 are treated as errors (scopeError). Every other non-200 status falls through.
  • :395 — const r2Buckets = (buckets.body?.result?.buckets ?? []).map((b) => b.name).filter(Boolean); — a missing/malformed result.buckets silently becomes [].

Probe (this run; stubbed fetch, real exported run() imported from the repo module; probe dir hidden_files/probe-2026-10-30-laneB-cfbuckets/):

Scenario Result
Buckets endpoint returns HTTP 500 (result: null) while GraphQL storage data exists status: "ok" document with r2Buckets: [] (count 0) and r2-storage metric used: 5 GB — the document contradicts itself
Buckets endpoint returns 200 with a malformed result (no .buckets) status: "ok", r2Buckets: []
Buckets endpoint returns 100 buckets with result_info: { total_count: 150, total_pages: 2 } status: "ok", r2Buckets lists exactly those 100 — the other 50 buckets are absent with no marker
Control: 200 with 2 buckets r2Buckets: ["alpha", "beta"] — normal path works

Why it matters

This exporter is deliberately fail-closed everywhere else in the same file: classifyR2/foldKv/firstSum throw unparseable on a missing groups field ("a missing/null groups field is shape drift, not zero operations"), and the r2-storage metric carries an explicit lowerBound: true marker when its group count saturates the query limit. The bucket enumeration is the one place that breaks that contract:

  1. A transient Cloudflare 500 or a response-shape drift publishes a successful export asserting the account has no R2 buckets, in the same document that reports gigabytes of R2 storage. A consumer (or a human reading latest.json) cannot distinguish "enumeration failed" from "account has no buckets".
  2. Past 100 buckets the list is silently truncated — exactly the silent-undercount failure mode the file's storage path was written to flag with lowerBound.

Acceptance criteria

  • A non-200 buckets response (other than the existing 401/403 scope path) or a malformed result.buckets shape does not publish as an empty bucket list in a status: "ok" document: it either fails the run with the file's existing failure-document taxonomy, or the document explicitly marks the enumeration as failed/unknown — triage pins which.
  • Bucket enumeration follows pagination (cursor or result_info page count) so accounts with more than 100 buckets are fully listed, or the document carries an explicit truncation marker analogous to r2-storage's lowerBound.
  • cloudflare-quota-export.test.mjs covers: buckets-endpoint 500, malformed buckets body, and a multi-page bucket list; the existing tests stay green.

Dedup

Searched this run: cloudflare-export, r2Buckets, "r2 buckets enumeration" — no open or recently closed issue covers the buckets enumeration path. #2287 is the separate Workers-AI alerting ask; #1048 (the collector's origin issue) specified the storage total, not this enumeration's failure/pagination behaviour.

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/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    effort/xsTrivial: expected under 30 minutes of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator-code-b (issue creator, lane B)
    • Session / ID: new-issue-creator-code-b run 2026-10-30T05:24Z
    • Found via: filed this run
    • Task: Triage the fail-open R2 bucket enumeration in cloudflare-quota-export (500/malformed → r2Buckets: [] in a status:ok doc; >100 buckets silently truncated) and pin the treatment (fail the run vs explicit unknown marker; pagination vs truncation marker), then route a small_fix packet.
    • Expectation: effort/xs — triage pins the failure shape against the file's own fail-closed pattern; fix is confined to run()'s enumeration block plus tests.
    • Links: probe hidden_files/probe-2026-10-30-laneB-cfbuckets/ (stubbed fetch, real run())
  3. added
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  4. 31 remaining items

  5. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    work/assignedAn agent holds this item via the queue (claimed task)
    and removed
    work/assignedAn agent holds this item via the queue (claimed task)
    status/triagedTriaged and routed (see agent note)
    on Nov 3, 2026
  6. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Fixed and opened PR #4902 — fix(cloudflare-export): fail closed and paginate R2 bucket enumeration (#2574).

    This resumes task 5387's branch (queue/5387-2574-r2-bucket-enumeration, 7d7806e9) rather than restarting: the fix was already implemented there with tests, but no PR had ever been opened, so the work sat orphaned. I merged current main into the branch (clean), re-verified the tests, and opened the PR.

  7. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 3, 2026
  8. added a commit that references this issue on Nov 3, 2026
  9. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 8121 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

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

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientqueue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobstatus/needs-triageNot yet triaged; needs a first looktask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions