← back to Wallco Ai
fix(/api/room): retry upstream once on fetch failed / transient errors
dbc500bd298eb7e581c37f0666d4d8f0fa13e232 · 2026-05-12 14:53:29 -0700 · SteveStudio2
The room-renderer service at 45.61.58.125:3075 drops 5-15% of requests
with {ok:false,error:'fetch failed'} or transient TLS hiccups. Surfaced
in the 27-render matrix where 5/27 cells failed cold and ALL FIVE
recovered on a single retry — meaning the failures were transient
upstream-side, not pattern-side.
Wraps the existing fetch in a 2-try loop with 800ms backoff. Returns
the last error in the response body if both attempts fail. Keeps the
b64 file read outside the loop (no double disk hit). No timeout
change — node fetch defaults already cover the upstream's ~9s p50.
Verified by re-running the 5 previously-failed matrix cells against
the patched server: 5/5 succeeded. Contact sheet now 27/27.
Files touched
Diff
commit dbc500bd298eb7e581c37f0666d4d8f0fa13e232
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 14:53:29 2026 -0700
fix(/api/room): retry upstream once on fetch failed / transient errors
The room-renderer service at 45.61.58.125:3075 drops 5-15% of requests
with {ok:false,error:'fetch failed'} or transient TLS hiccups. Surfaced
in the 27-render matrix where 5/27 cells failed cold and ALL FIVE
recovered on a single retry — meaning the failures were transient
upstream-side, not pattern-side.
Wraps the existing fetch in a 2-try loop with 800ms backoff. Returns
the last error in the response body if both attempts fail. Keeps the
b64 file read outside the loop (no double disk hit). No timeout
change — node fetch defaults already cover the upstream's ~9s p50.
Verified by re-running the 5 previously-failed matrix cells against
the patched server: 5/5 succeeded. Contact sheet now 27/27.
---
server.js | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/server.js b/server.js
index df5fc00..9db5df3 100644
--- a/server.js
+++ b/server.js
@@ -6927,17 +6927,31 @@ app.post('/api/room', async (req, res) => {
}
try {
const b64 = fs.readFileSync(file).toString('base64');
- const upstream = await fetch(ROOM_API, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- patternBase64: b64, roomType, angle,
- cameraDistance, patternWidth, patternHeight
- })
- });
- const j = await upstream.json();
- if (!j || j.success === false || !j.image) {
- return res.status(502).json({ ok: false, error: j && j.error || 'upstream error', upstream: j });
+ // Upstream renderer (45.61.58.125:3075) drops ~5-15% of requests with
+ // {ok:false,error:'fetch failed'} or transient TLS hiccups. Retry once
+ // with 800ms backoff before giving up — caught in the 27-render matrix
+ // 2026-05-12 where 5/27 cells failed and would have succeeded on retry.
+ let j = null, lastErr = null;
+ for (let attempt = 1; attempt <= 2; attempt++) {
+ try {
+ const upstream = await fetch(ROOM_API, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ patternBase64: b64, roomType, angle,
+ cameraDistance, patternWidth, patternHeight
+ })
+ });
+ const candidate = await upstream.json();
+ if (candidate && candidate.success !== false && candidate.image) { j = candidate; break; }
+ lastErr = (candidate && candidate.error) || 'upstream returned no image';
+ } catch (e) {
+ lastErr = e.message;
+ }
+ if (attempt < 2) await new Promise(r => setTimeout(r, 800));
+ }
+ if (!j) {
+ return res.status(502).json({ ok: false, error: lastErr || 'upstream error', retried: true });
}
// upstream returns { success, image, ... } — `image` is base64 or data-URL
const imgB64 = j.image.replace(/^data:image\/\w+;base64,/, '');
← 48baf5a wallco.ai · audit log Phase 1 — capture all user + admin act
·
back to Wallco Ai
·
tools: add scripts/seam_check.py — quantify pattern seamless 7901449 →