← back to Designer Wallcoverings
Fix silent-throttle bug in mfr push: detect top-level errors/null-data/429, retry w/ backoff, adaptive pacing, honest accounting + failed.csv
0c1bf6d1cf22bfbf28d528222a4ee5a6036cc6f3 · 2026-07-12 10:06:09 -0700 · Steve
Files touched
M scripts/houston-mfr-fix.js
Diff
commit 0c1bf6d1cf22bfbf28d528222a4ee5a6036cc6f3
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jul 12 10:06:09 2026 -0700
Fix silent-throttle bug in mfr push: detect top-level errors/null-data/429, retry w/ backoff, adaptive pacing, honest accounting + failed.csv
---
scripts/houston-mfr-fix.js | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/scripts/houston-mfr-fix.js b/scripts/houston-mfr-fix.js
index 804aa159..b6e791f6 100644
--- a/scripts/houston-mfr-fix.js
+++ b/scripts/houston-mfr-fix.js
@@ -116,25 +116,39 @@ async function main() {
if (!EXECUTE) { console.log(`\n(dry run — no Shopify writes. re-run with --execute to push)\n`); await pool.end(); return; }
- // 2. GATED execution path
- let ok = 0, err = 0;
+ // 2. GATED execution path — throttle-aware + retry + HONEST accounting
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
+ let ok = 0, err = 0; const failed = [];
for (const m of manifest) {
const gid = m.shopify_id.startsWith('gid://') ? m.shopify_id : `gid://shopify/Product/${m.shopify_id}`;
- const body = { query: `mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){userErrors{field message}}}`,
+ const body = { query: `mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){metafields{id} userErrors{field message}}}`,
variables: { mf: [
{ ownerId: gid, namespace: 'custom', key: 'manufacturer_sku', value: m.new, type: 'single_line_text_field' },
{ ownerId: gid, namespace: 'dwc', key: 'manufacturer_sku', value: m.new, type: 'single_line_text_field' } ] } };
- 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?.metafieldsSet?.userErrors || [];
- if (ue.length) { err++; console.error(` ERR ${m.dw_sku}: ${JSON.stringify(ue)}`); }
- else { ok++; if (ok % 250 === 0) console.log(` ...${ok} pushed`); }
- } catch (e) { err++; console.error(` ERR ${m.dw_sku}: ${e.message}`); }
- await new Promise(r => setTimeout(r, 60)); // gentle throttle
+ 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();
+ // THROTTLED / transport failure → top-level errors or null data: back off + retry (was silently counted OK before)
+ if (res.status === 429 || j.errors || !j.data || !j.data.metafieldsSet) {
+ lastErr = `throttle/${res.status}:${JSON.stringify(j.errors||'null-data')}`; await sleep(1500 * attempt); continue;
+ }
+ const ue = j.data.metafieldsSet.userErrors || [];
+ if (ue.length) { lastErr = JSON.stringify(ue); break; } // real userError → don't retry
+ if (!j.data.metafieldsSet.metafields?.length) { lastErr = 'no-metafields-returned'; await sleep(1500*attempt); continue; }
+ wrote = true;
+ const avail = j.extensions?.cost?.throttleStatus?.currentlyAvailable ?? 1000; // adaptive pacing off the real bucket
+ 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} pushed`); }
+ else { err++; failed.push(m.dw_sku); console.error(` ERR ${m.dw_sku}: ${lastErr}`); }
}
console.log(`\nDONE — pushed ${ok}, errors ${err}. Also re-sync the local mirror.`);
+ if (failed.length) { fs.writeFileSync(path.join(outDir, 'mfr-fix-failed.csv'), 'dw_sku\n' + failed.join('\n'));
+ console.log(` ${failed.length} failures → reports/mfr-houston/mfr-fix-failed.csv`); }
await pool.end();
}
main().catch(e => { console.error(e); process.exit(1); });
← 60525f46 PR quote-only tagging tool + manifest (DTD verdict C: quote-
·
back to Designer Wallcoverings
·
auto-save: 2026-07-12T10:17:36 (5 files) — pending-approval/ d1ffa01d →