[object Object]

← 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 →