← back to Wallco Ai
ghost-detector: bump 429 retry budget 4→6 attempts
2d3c47cf0ee99f0e8a9b9d48107b9b4cc4566800 · 2026-05-24 00:12:36 -0700 · Steve Abrams
Five parallel triage processes saturated Gemini Flash beyond what the
4-attempt backoff (1s/2s/4s = 7s max wait) could absorb — 847 of 1,718
damask re-triage rows errored on 429.
Bump to 6 attempts (1s/2s/4s/8s/16s = ~31s max wait). Same retry shape,
just two more rungs on the backoff ladder. Single-process runs unaffected
since they don't hit the limit; only matters when multiple scripts run
concurrently against the same key.
Files touched
Diff
commit 2d3c47cf0ee99f0e8a9b9d48107b9b4cc4566800
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 24 00:12:36 2026 -0700
ghost-detector: bump 429 retry budget 4→6 attempts
Five parallel triage processes saturated Gemini Flash beyond what the
4-attempt backoff (1s/2s/4s = 7s max wait) could absorb — 847 of 1,718
damask re-triage rows errored on 429.
Bump to 6 attempts (1s/2s/4s/8s/16s = ~31s max wait). Same retry shape,
just two more rungs on the backoff ladder. Single-process runs unaffected
since they don't hit the limit; only matters when multiple scripts run
concurrently against the same key.
---
lib/ghost-detector.js | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/lib/ghost-detector.js b/lib/ghost-detector.js
index dc9fadf..aeaf7e8 100644
--- a/lib/ghost-detector.js
+++ b/lib/ghost-detector.js
@@ -149,10 +149,12 @@ async function analyzeGhostLayer(imagePath, opts = {}) {
};
// Retry-on-429 (and 5xx) with exponential backoff. Gemini Flash returns
- // 429 "Resource exhausted" under high parallel load; one short pause is
- // usually enough. Max 3 retries: 1s, 2s, 4s.
+ // 429 "Resource exhausted" under high parallel load. 5 retries cover up
+ // to ~31s of backoff (1s/2s/4s/8s/16s) — enough to ride out the quota
+ // refresh window when multiple processes saturate the endpoint.
let r, lastTxt = '';
- for (let attempt = 0; attempt < 4; attempt++) {
+ const MAX_ATTEMPTS = 6;
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
r = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -161,7 +163,7 @@ async function analyzeGhostLayer(imagePath, opts = {}) {
if (r.ok) break;
lastTxt = await r.text().catch(() => '');
if (r.status !== 429 && r.status < 500) break; // non-retryable
- if (attempt === 3) break;
+ if (attempt === MAX_ATTEMPTS - 1) break;
await new Promise(res => setTimeout(res, 1000 * Math.pow(2, attempt) + Math.random() * 500));
}
if (!r.ok) {
← a9fbc70 ghost-review: SHIFT+drag = KEEP element (green dotted) · fix
·
back to Wallco Ai
·
feat(ghost-review): bulk-select with marquee drag + Delete & b6c85f3 →