← back to Dw Five Field Step0
fivefield drain: clean 429 back-off on Shopify daily variant limit
8339519f49dcabd0ddb079d6ac07ec8d466d07c6 · 2026-06-23 10:07:17 -0700 · Steve Studio
On the 'Daily variant creation limit' 429, raise a BaseException sentinel that the
main loop catches → stop the run immediately + refund the unused backlog grant,
instead of grinding the whole granted slice into thousands of failed 429s (11,754
in one day, created ~0, monopolized the shared cap). Cap resets in ~24h; resumable.
Files touched
Diff
commit 8339519f49dcabd0ddb079d6ac07ec8d466d07c6
Author: Steve Studio <steve@designerwallcoverings.com>
Date: Tue Jun 23 10:07:17 2026 -0700
fivefield drain: clean 429 back-off on Shopify daily variant limit
On the 'Daily variant creation limit' 429, raise a BaseException sentinel that the
main loop catches → stop the run immediately + refund the unused backlog grant,
instead of grinding the whole granted slice into thousands of failed 429s (11,754
in one day, created ~0, monopolized the shared cap). Cap resets in ~24h; resumable.
---
bulk-fivefield-exec.py | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/bulk-fivefield-exec.py b/bulk-fivefield-exec.py
index a52f574..2fb47f3 100644
--- a/bulk-fivefield-exec.py
+++ b/bulk-fivefield-exec.py
@@ -33,6 +33,13 @@ DOES NOT mutate the worklist table. Audit JSON is the resume ledger.
"""
import json, urllib.request, urllib.error, subprocess, time, os, sys, argparse
+
+# Sentinel for Shopify's DAILY variant-creation 429. Inherits BaseException (NOT Exception) so the
+# per-SKU `except Exception` inside process() can't swallow it — it propagates straight to main()'s
+# handler, which stops the run cleanly + refunds. (2026-06-23 DTD-C 429-back-off.)
+class DailyVariantLimit(BaseException):
+ pass
+
DOMAIN = "designer-laboratory-sandbox.myshopify.com"
API = "2024-10"
OUTDIR = "/Users/stevestudio2/Projects/dw-five-field-step0/out"
@@ -231,6 +238,16 @@ def process(item, results, dry_run=False):
# --- create the missing variant ---
st2, r2 = shop("POST", f"products/{pid}/variants.json", vbody)
if st2 not in (200, 201):
+ # 2026-06-23 (DTD-C / Steve): on Shopify's DAILY variant-creation 429, STOP the whole
+ # run cleanly instead of grinding the rest of the granted slice into thousands of failed
+ # 429s (11,754 in one day → it monopolized the cap creating ~0). The cap resets in ~24h;
+ # the unused grant is refunded by main()'s refund-rule path. Raise a sentinel the loop
+ # catches → records this SKU as errored:daily_limit, refunds, exits.
+ body_txt = json.dumps(r2)
+ if st2 == 429 and "Daily variant creation limit" in body_txt:
+ rec["status"] = "errored"; rec["reason"] = "daily_variant_limit_429_backoff"; rec["api"] = r2
+ results.append(rec); print(f"BACKOFF {dw}: Daily variant creation limit 429 — stopping run cleanly (resets ~24h)")
+ raise DailyVariantLimit(dw)
rec["status"] = "errored"; rec["reason"] = f"create_{st2}"; rec["api"] = r2
results.append(rec); print(f"ERR {dw}: create {st2} {r2}"); return 0
nv = r2.get("variant", {})
@@ -316,7 +333,15 @@ def main():
break
if args.dry_run and processed >= granted:
break
- made = process(w, results, dry_run=args.dry_run)
+ try:
+ made = process(w, results, dry_run=args.dry_run)
+ except DailyVariantLimit:
+ # Shopify's daily variant cap is hit — stop NOW (no point grinding the rest of the
+ # grant into 429s). The refund path below returns the whole unused grant to budget.cjs.
+ json.dump(results, open(RESULT, "w"), indent=1)
+ print(f"\n[backoff] Daily variant creation limit reached — stopping run cleanly after "
+ f"{created} create(s) this run. Resumable next budget window (~24h reset).")
+ break
created += made
processed += 1
if not args.dry_run:
@@ -333,7 +358,7 @@ def main():
unused = granted - created
if unused > 0:
got = budget_refund(unused)
- print(f"[budget] refund: granted={granted} used={created} -> refunded {got} upload variants.")
+ print(f"[budget] refund: granted={granted} used={created} -> refunded {got} {BUDGET_CAT} variants.")
# --- summary ---
fixed = sum(1 for r in results if r["status"] == "fixed")
← 750a3a7 auto-save: 2026-06-23T09:20:41 (1 files) — out/bulk-fivefiel
·
back to Dw Five Field Step0
·
auto-save: 2026-06-23T10:21:01 (2 files) — out/bulk-fivefiel 35f153f →