[object Object]

← back to Designer Wallcoverings

Fix same silent-throttle bug in quote-only tagging: throttle/429/null-data detect + retry + failed.csv

6d39087c5a469b04bb77dec437aff02586aadbf8 · 2026-07-12 10:35:45 -0700 · Steve

Files touched

Diff

commit 6d39087c5a469b04bb77dec437aff02586aadbf8
Author: Steve <steve@designerwallcoverings.com>
Date:   Sun Jul 12 10:35:45 2026 -0700

    Fix same silent-throttle bug in quote-only tagging: throttle/429/null-data detect + retry + failed.csv
---
 scripts/pr-quote-only-tag.js | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/scripts/pr-quote-only-tag.js b/scripts/pr-quote-only-tag.js
index a5239c53..3b2285e3 100644
--- a/scripts/pr-quote-only-tag.js
+++ b/scripts/pr-quote-only-tag.js
@@ -44,24 +44,36 @@ async function main() {
   console.log(`  cost       : $0 (local + Shopify Admin API)`);
   if (!EXECUTE) { console.log('\n(dry run — no Shopify writes)\n'); await pool.end(); return; }
 
-  let ok = 0, err = 0;
+  const sleep = ms => new Promise(r => setTimeout(r, ms));
+  let ok = 0, err = 0; const failed = [];
   for (const r of rows) {
     const gid = r.shopify_id.startsWith('gid://') ? r.shopify_id : `gid://shopify/Product/${r.shopify_id}`;
     const tags = (r.tags ? r.tags.split(',').map(t => t.trim()).filter(Boolean) : []);
     if (!tags.some(t => /quote/i.test(t))) tags.push('quotes');
-    const body = { query: `mutation($id:ID!,$tags:[String!]!){productUpdate(input:{id:$id,tags:$tags}){userErrors{message}}}`,
+    const body = { query: `mutation($id:ID!,$tags:[String!]!){productUpdate(input:{id:$id,tags:$tags}){product{id} userErrors{message}}}`,
       variables: { id: gid, tags } };
-    try {
-      const res = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`,
-        { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
-      const j = await res.json();
-      const ue = j?.data?.productUpdate?.userErrors || [];
-      if (ue.length) { err++; console.error(`  ERR ${r.dw_sku}: ${JSON.stringify(ue)}`); }
-      else { ok++; if (ok % 250 === 0) console.log(`  ...${ok} tagged`); }
-    } catch (e) { err++; console.error(`  ERR ${r.dw_sku}: ${e.message}`); }
-    await new Promise(r => setTimeout(r, 60));
+    let wrote = false, lastErr = '';
+    for (let attempt = 1; attempt <= 6 && !wrote; attempt++) {
+      try {
+        const res = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`,
+          { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
+        const j = await res.json();
+        if (res.status === 429 || j.errors || !j.data || !j.data.productUpdate) {   // throttle/transport → back off + retry
+          lastErr = `throttle/${res.status}:${JSON.stringify(j.errors||'null-data')}`; await sleep(1500 * attempt); continue; }
+        const ue = j.data.productUpdate.userErrors || [];
+        if (ue.length) { lastErr = JSON.stringify(ue); break; }                      // real userError → don't retry
+        if (!j.data.productUpdate.product?.id) { lastErr = 'no-product-returned'; await sleep(1500*attempt); continue; }
+        wrote = true;
+        const avail = j.extensions?.cost?.throttleStatus?.currentlyAvailable ?? 1000;
+        await sleep(avail < 300 ? 800 : 220);
+      } catch (e) { lastErr = e.message; await sleep(1500 * attempt); }
+    }
+    if (wrote) { ok++; if (ok % 250 === 0) console.log(`  ...${ok} tagged`); }
+    else { err++; failed.push(r.dw_sku); console.error(`  ERR ${r.dw_sku}: ${lastErr}`); }
   }
   console.log(`\nDONE — tagged ${ok}, errors ${err}. Re-sync the local mirror.`);
+  if (failed.length) { fs.writeFileSync(path.join(outDir, 'quote-only-failed.csv'), 'dw_sku\n'+failed.join('\n'));
+    console.log(`  ${failed.length} failures → reports/mfr-houston/quote-only-failed.csv`); }
   await pool.end();
 }
 main().catch(e => { console.error(e); process.exit(1); });

← d1ffa01d auto-save: 2026-07-12T10:17:36 (5 files) — pending-approval/  ·  back to Designer Wallcoverings  ·  auto-save: 2026-07-12T10:47:45 (5 files) — pending-approval/ c132a2ee →