← back to Designer Wallcoverings
drain self-heal round 2: missing playwright browsers treated as env failure (refund attempt, auto npx playwright install in ensure-import-app, resurrect pattern widened); app restarted onto playwright 1.57 binaries
615561a2fe53e0d72a5429d4d02573ec87990083 · 2026-07-13 08:35:31 -0700 · Steve
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Files touched
M DW-Programming/ImportNewSkufromURL/scripts/ensure-import-app.shM DW-Programming/ImportNewSkufromURL/scripts/import-queue-runner.jsM DW-Programming/ImportNewSkufromURL/scripts/resurrect-infra-failed.js
Diff
commit 615561a2fe53e0d72a5429d4d02573ec87990083
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jul 13 08:35:31 2026 -0700
drain self-heal round 2: missing playwright browsers treated as env failure (refund attempt, auto npx playwright install in ensure-import-app, resurrect pattern widened); app restarted onto playwright 1.57 binaries
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
.../ImportNewSkufromURL/scripts/ensure-import-app.sh | 9 +++++++++
.../ImportNewSkufromURL/scripts/import-queue-runner.js | 13 ++++++++-----
.../ImportNewSkufromURL/scripts/resurrect-infra-failed.js | 2 +-
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/DW-Programming/ImportNewSkufromURL/scripts/ensure-import-app.sh b/DW-Programming/ImportNewSkufromURL/scripts/ensure-import-app.sh
index 68fb0d0d..3a675098 100755
--- a/DW-Programming/ImportNewSkufromURL/scripts/ensure-import-app.sh
+++ b/DW-Programming/ImportNewSkufromURL/scripts/ensure-import-app.sh
@@ -19,6 +19,15 @@ alive() {
[ -n "$code" ] && [ "$code" != "000" ]
}
+# ENV HEAL: scrapes die with "Browser launch failed: Executable doesn't exist"
+# when a playwright update outpaces its downloaded browsers (bit us 2026-07-13,
+# burning items as failed while the app itself was healthy). Idempotent + quick
+# when binaries already exist.
+if ! ls "$HOME/Library/Caches/ms-playwright"/chromium_headless_shell-*/chrome-headless-shell-mac-*/chrome-headless-shell >/dev/null 2>&1; then
+ echo "[ensure-import-app] playwright headless-shell missing — installing"
+ (cd "$APP_DIR" && npx playwright install chromium chromium-headless-shell >/dev/null 2>&1)
+fi
+
alive && exit 0
echo "[ensure-import-app] :9830 dead — attempting pm2 heal $(date)"
diff --git a/DW-Programming/ImportNewSkufromURL/scripts/import-queue-runner.js b/DW-Programming/ImportNewSkufromURL/scripts/import-queue-runner.js
index 9dc799c7..c0d38ac9 100644
--- a/DW-Programming/ImportNewSkufromURL/scripts/import-queue-runner.js
+++ b/DW-Programming/ImportNewSkufromURL/scripts/import-queue-runner.js
@@ -103,7 +103,8 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// shared ensure-import-app.sh (pm2 restart/start + poll); if it can't come back,
// exit 69 (EX_UNAVAILABLE) so the drain driver sleeps and retries the whole pass.
const { execFileSync } = require('child_process');
-const BACKEND_DOWN_RE = /ECONNREFUSED|ECONNRESET|socket hang up/i;
+const BACKEND_DOWN_RE = /ECONNREFUSED|ECONNRESET|socket hang up|Browser launch failed|Executable doesn't exist|playwright install/i;
+let envFailStreak = 0; // consecutive environment failures — bail at 3 so we don't spin forever
function healBackend() {
try {
execFileSync('/bin/sh', [require('path').join(__dirname, 'ensure-import-app.sh')], { stdio: 'inherit', timeout: 180000 });
@@ -233,15 +234,17 @@ if (!QUEUE || !fs.existsSync(QUEUE)) { console.error('usage: import-queue-runner
// driver retries the pass later instead of grinding the queue to failed.
if (BACKEND_DOWN_RE.test(note)) {
it.attempts--;
- it.note = note + ' (backend down — attempt refunded)';
- console.log(` ⚕ ${it.sku}: ${note} — backend down, healing (attempt not charged)`);
+ it.note = note.slice(0, 200) + ' (env failure — attempt refunded)';
+ console.log(` ⚕ ${it.sku}: env failure — healing (attempt not charged)`);
save(items);
- if (!healBackend()) {
- console.log('[import-queue] backend unhealable — exiting 69 (EX_UNAVAILABLE)');
+ envFailStreak++;
+ if (envFailStreak >= 3 || !healBackend()) {
+ console.log('[import-queue] environment unhealable this pass — exiting 69 (EX_UNAVAILABLE)');
process.exit(69);
}
continue; // same pass resumes with the next pending item
}
+ envFailStreak = 0;
// TRANSIENT one-off drops (e.g. request timeout mid-recompile) must NOT
// permanently fail on one shot — leave pending.
const transient = /timeout/i.test(note);
diff --git a/DW-Programming/ImportNewSkufromURL/scripts/resurrect-infra-failed.js b/DW-Programming/ImportNewSkufromURL/scripts/resurrect-infra-failed.js
index 551a7bdb..9be65ff6 100644
--- a/DW-Programming/ImportNewSkufromURL/scripts/resurrect-infra-failed.js
+++ b/DW-Programming/ImportNewSkufromURL/scripts/resurrect-infra-failed.js
@@ -14,7 +14,7 @@ const fs = require('fs');
const path = require('path');
const DIR = process.argv[2] || 'data/scraper-audit/reconcile';
-const INFRA_RE = /ECONNREFUSED|ECONNRESET|socket hang up|backend down|timeout/i;
+const INFRA_RE = /ECONNREFUSED|ECONNRESET|socket hang up|backend down|env failure|timeout|Browser launch failed|Executable doesn't exist|playwright install/i;
let total = 0;
for (const f of fs.readdirSync(DIR).filter(f => f.endsWith('-import-queue.jsonl'))) {
← 84bb022a import drain self-heals: backend-down never burns item attem
·
back to Designer Wallcoverings
·
chore: session-close quality gate — hoist sleep/isSample to 91304e05 →