[object Object]

← back to Wolfgordon Crawl

Harden pusher: graceful defer on Shopify daily variant-create quota

e28d26a70ff82d6ad926297aceb3a8e7c665e807 · 2026-06-19 10:15:35 -0700 · Steve

Canary live: 3 UPDATE (price+SKU) + 1 ARCHIVE landed; 5 variant-creating items deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit e28d26a70ff82d6ad926297aceb3a8e7c665e807
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Jun 19 10:15:35 2026 -0700

    Harden pusher: graceful defer on Shopify daily variant-create quota
    
    Canary live: 3 UPDATE (price+SKU) + 1 ARCHIVE landed; 5 variant-creating items deferred.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 push-shopify.js | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/push-shopify.js b/push-shopify.js
index e905a94..af917f1 100644
--- a/push-shopify.js
+++ b/push-shopify.js
@@ -66,7 +66,12 @@ async function gql(query, variables) {
     body: JSON.stringify({ query, variables }),
   });
   const j = await res.json();
-  if (j.errors) throw new Error('GraphQL: ' + JSON.stringify(j.errors));
+  if (j.errors) {
+    const throttled = JSON.stringify(j.errors).includes('VARIANT_THROTTLE_EXCEEDED');
+    const err = new Error('GraphQL: ' + JSON.stringify(j.errors));
+    err.variantThrottled = throttled;
+    throw err;
+  }
   return j.data;
 }
 
@@ -435,14 +440,20 @@ async function main() {
   console.log('-'.repeat(74));
 
   const results = { new: [], update: [], draft: [], archive: [] };
+  const deferred = []; // items blocked by Shopify daily variant-create quota
 
   // NEW
   for (const r of work.new) {
     const t = buildTitle(r);
     console.log(`  [NEW]   ${r.dw_sku} ${t}  $${r.price_retail}`);
     if (APPLY) {
-      const p = await createProduct(r);
-      if (p) results.new.push({ handle: p.handle, sku: r.dw_sku, price: String(r.price_retail), status: p.status });
+      try {
+        const p = await createProduct(r);
+        if (p) results.new.push({ handle: p.handle, sku: r.dw_sku, price: String(r.price_retail), status: p.status });
+      } catch (e) {
+        if (e.variantThrottled) { console.log('    DEFERRED — daily variant-create quota reached'); deferred.push({ action: 'new', sku: r.dw_sku }); }
+        else throw e;
+      }
     }
   }
   // DRAFT (quote-only, null price)
@@ -450,8 +461,13 @@ async function main() {
     const t = buildTitle(r);
     console.log(`  [DRAFT] ${r.dw_sku} ${t}  (quote-only)`);
     if (APPLY) {
-      const p = await createProduct(r, { draft: true });
-      if (p) results.draft.push({ handle: p.handle, sku: r.dw_sku, price: 'quote-only', status: p.status });
+      try {
+        const p = await createProduct(r, { draft: true });
+        if (p) results.draft.push({ handle: p.handle, sku: r.dw_sku, price: 'quote-only', status: p.status });
+      } catch (e) {
+        if (e.variantThrottled) { console.log('    DEFERRED — daily variant-create quota reached'); deferred.push({ action: 'draft', sku: r.dw_sku }); }
+        else throw e;
+      }
     }
   }
   // UPDATE
@@ -471,6 +487,10 @@ async function main() {
   } else {
     console.log('  APPLIED. Results:');
     console.log(JSON.stringify(results, null, 2));
+    if (deferred.length) {
+      console.log(`  DEFERRED (Shopify daily variant-create quota, retry tomorrow): ${deferred.length}`);
+      console.log('  ' + JSON.stringify(deferred));
+    }
   }
   await pool.end();
 }

← a9497fc Add clean catalog-driven Wolf Gordon Shopify pusher (dry-run  ·  back to Wolfgordon Crawl  ·  Add --phase1 (variant-free update+archive) + --new-only/--ma 4be3ecc →