Repository navigation
Description
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 andresult_infois 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/malformedresult.bucketssilently 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:
- 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". - 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.bucketsshape does not publish as an empty bucket list in astatus: "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_infopage count) so accounts with more than 100 buckets are fully listed, or the document carries an explicit truncation marker analogous to r2-storage'slowerBound. -
cloudflare-quota-export.test.mjscovers: 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
dezren39 commentedon Oct 30, 2026
🤖 Agent note · filed
31 remaining items
dezren39 commentedon Nov 3, 2026
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 currentmaininto the branch (clean), re-verified the tests, and opened the PR.result.buckets, a full page with no pagination metadata, or a later-page failure now publishesr2Buckets: null+r2BucketsDetail(page + reason) instead of a silent[]or a truncated list; 401/403 still throws the existing token-scope error.result_info.total_pagesis followed.node --test pm-stack/cloudflare-export/cloudflare-quota-export.test.mjs— 33 pass / 0 fail (seven new 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 cases: 500, malformed body, multi-page, later-page failure, malformed later page, full-page-no-metadata, 401/403 contract).Merge pull request #4902 from developing-today/queue/5387-2574-r2-buc…
developing-today commentedon Nov 3, 2026
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
Merge origin/main into queue/6120-3469-strict-quota-export-argv