Closed bug report

Cold start (~8s, warmup before listen) is reaped before bind — pup 100% unusable on adom_drew2 (adom-desktop#31 Part 3)

Drew Owens · 5d ago ·closed by John Lauer

TL;DR

On adom_drew2 the puppeteer bridge is 100% unusable: every browser_* verb returns bridge_not_listening, AD reaps the process at ~2–3.6s, and it never recovers — a full AD restart does not help, and the bridge is already on the latest (1.9.333).

Root cause, proven below: the bridge is healthy but slow to bind. Launched directly it binds and completes startup in ~8s; AD's recover_not_listening reaps it at ~2–3.6s, before it reaches its listen call. So AD is killing a still-warming-up bridge on every spawn. This is the bridge-side "Part 3" that was routed here from adom/adom-desktop#31 ("bind the listener BEFORE the startup work").

Environment

  • AD 1.9.228, target adom_drew2 (Windows, drew), via relay from a cloud container
  • puppeteer bridge 1.9.333, source=cache, persistent=false, stable port 57134 → reassigned 8851 after AD restart
  • Date: 2026-08-05

Symptom

Every browser_open_window (and any pup verb) returns:

errorCode: "bridge_not_listening"
error: "The 'puppeteer' bridge process was running but never bound its port (57134). AD reaped it (0 pid(s)); RETRY your verb..."
processWasLive: false, killedPids: [], durationMs: ~2100–3700
  • Retried many times across two AD states — identical every time.
  • Full AD restart did not fix it: the stable port moved 57134 → 8851 (confirming a clean restart), and it failed identically on 8851.
  • refresh_bridges {puppeteer}action: current (already 1.9.333); no update to pull.

What it is NOT (ruled out)

  • C:\Users\drew\.adom\pup-sessions\ is empty (0 files) — no stale session store to recover.
  • C:\Users\drew\.adom\recovery\ contains only fusion — nothing pup.
  • No *Singleton* / *.lock files found under C:\Users\drew\.adom\pup-profiles\ (42 profile dirs). (Windows Chrome locking differs from Unix SingletonLock, so this is suggestive, not conclusive — but there is no obvious stale lock.)
  • Target port was free before each spawn (netstat showed nothing LISTENING on it).

So unlike adom-desktop#31's original incident, there is no stale session/profile lock here — yet it still wedges.

Root cause — proven by launching the bridge directly

Ran the bridge's own entrypoint directly (detached, logging to a file):

cd "C:\Users\drew\AppData\Local\Adom Desktop\bridges-cache\puppeteer"
node server.js   # → %TEMP%\pupdirect.log

After ~8s the log shows a fully healthy startup that DID bind:

[recorder] ffmpeg available at: ffmpeg
Puppeteer Bridge running on http://127.0.0.1:8851 (bind=127.0.0.1; no firewall prompt)
Multi-session support: sessions are tabs sharing Chrome profiles via userDataDir
Endpoints: /health, /command, /launch, /navigate, ...
[warmup] browsers: chrome(system), edge(system), chrome-for-testing(cache) | cft cached: true | disk free: 1592233MB
[notch] ready (6 tiles, notch=50% upper-right) — forcing a re-stamp so buttons pick it up
[adver] AD 1.9.228
[identity] Adom.Pup registered (HKCU AUMID + Start Menu pin -> managed pup window)
[drag] subscribed to window-opened (watchId=w1) — dragged-out tabs adopt themselves

It bound 8851 (exactly the port AD assigned) and stayed up. So the bridge is not wedged — it just takes ~8s to get to its listen call, because a lot of startup work (browser warmup/detection, notch stamp, AUMID/identity registration, drag subscription) runs before the HTTP listener is up.

Meanwhile AD's spawn path reaps it at ~2–3.6s. AD is killing a healthy bridge mid-warmup, before it can bind. Every verb triggers a fresh spawn → same ~2–3.6s reap → the ~8s startup never completes under AD. That's the loop.

Why the earlier fix (adom-desktop#31) doesn't cover this

adom-desktop#31 fixed the AD-core halves (status lying; adding recover_not_listening + bridge_not_listening), and John explicitly routed the trigger here: "the process coming up but never listening ... is pup's to fix (bind the listener BEFORE the recovery work)." This report confirms that routing and sharpens it:

  • The blocker is not a stale lock this time — it's the ordering + duration of normal startup. Warmup/identity/notch run before listen(), pushing first-bind to ~8s.
  • recover_not_listening currently can't tell "still cold-starting (~8s)" from "wedged forever," so it reaps a bridge that would have bound a few seconds later.

Suggested fix

Bridge-side (primary, this page): bind the HTTP listener first, before any warmup/browser-detection/notch/identity/session-recovery work; run all of that asynchronously after /health is answerable. Then AD's short probe succeeds within <1s and the ~8s of warmup happens in the background. This is the generalized form of #31's "bind before recovery."

AD-side (cross-ref adom-desktop#31, secondary): give a cold-starting process a longer first-spawn grace (health-wait ~10–15s) before recover_not_listening reaps it, so a merely-slow start isn't treated as a wedge. Even with the bridge fix, this would harden against slow boxes.

Repro

adom-desktop --target adom_drew2 browser_open_window '{"sessionId":"x","url":"https://example.com"}'
# → bridge_not_listening, reaped ~2–3.6s, every time; survives a full AD restart

# Meanwhile, directly:
#   cd bridges-cache\puppeteer && node server.js
# → "Puppeteer Bridge running on http://127.0.0.1:<assignedPort>" after ~8s (healthy)

Impact

pup is completely unusable on this machine — no screenshots, no page verification, no automation — and the AI-facing recovery (retry, refresh, full AD restart) all fail. The only thing that binds the bridge is launching it by hand, which AD then reaps.

1 Reply

John Lauer · 4d ago

Fixed in bridge v1.9.334 — bind the port BEFORE the heavy native loading

Confirmed root cause and shipped the bridge-side fix (the "Part 3" routed here from adom/adom-desktop#31).

Root cause (as diagnosed here): the heavy NATIVE requires ran synchronously at module load, blocking the event loop before server.listen() could bind:

  • require('puppeteer')
  • require('./credential_vault')keytar
  • require('sharp')
  • require('./chrome') — and chrome.js itself does require('puppeteer') at its top

On a slow box that's ~8s of synchronous work before the first tick that completes the bind, so AD's recover_not_listening (a TCP listen check) reaps at ~2–3.6s, before the port is ever LISTENING.

Fix (v1.9.334): all four heavy requires are deferred out of the synchronous module-load path into a single ensureHeavyInit(), which runs:

  1. as the first line of the server.listen callback (fires AFTER the OS bind, so the port is already LISTENING and the post-bind native load is safe), and
  2. lazily on the first non-/health request (belt-and-suspenders).

/health needs no heavy module — its chrome.readiness() is guarded to report {initializing:true} during the pre-init window.

Measured, same repro (node server.js run directly):

BRIDGE_VERSION: 1.9.334
Puppeteer Bridge running on http://127.0.0.1:8899   ← BIND at 0.16s   (was ~8s)
[warmup] browsers: ...                              ← heavy init, AFTER bind
[notch] ready (6 tiles) ...                         (done at 0.45s)

The port now binds in 0.16s vs ~8s, comfortably inside AD's grace even on a slow box (the native loading, however long, happens after the port is listening).

The secondary AD-side hardening (a longer first-spawn health grace, ~10–15s) is still worth doing per the report, but is no longer required for pup to be usable. Closing as fixed on the bridge side.

Log in to reply.