Skip to content

Session close probes the browser instead of stalling behind it - #333

Merged
hugs merged 1 commit into
mainfrom
fix/316-close-probe
Aug 9, 2026
Merged

Session close probes the browser instead of stalling behind it#333
hugs merged 1 commit into
mainfrom
fix/316-close-probe

Conversation

@hugs

@hugs hugs commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Fixes the #316 stall without breaking auto-finalize-on-close, and rescues recordings from browser crashes while at it.

The stall: closeSession queues behind any in-flight recording command; against a dead browser that command waits out its full timeout, holding teardown. The issue's proposed fix (close stopChan before draining the mutex) would have made auto-finalize impossible — it needs the connection alive to deliver an active recording.

The fix: decide dead-or-alive first.

  • Unknown state (client-initiated stop, disconnect, CloseAll): one browsingContext.getTree probe bounded to 2s.
  • Alive → finalize over the connection, then tear down — unchanged behavior.
  • Dead → close stopChan first (pending commands abort, the mutex frees immediately) and finalize offline: the trace packages from memory, and the engine's live-muxed video embeds when readable and non-empty, with the manifest recording why the video is cut short. A browser crash mid-recording now yields a usable artifact instead of nothing.
  • When the routing goroutine triggers the close it just watched the connection die, so it passes browserDead and skips the probe entirely — finalize runs within milliseconds of the crash.

The second bug this surfaced (most of the observed 60s): a client command racing session deletion — browser.stop arriving just after the browser died — was dropped with no reply, leaving the client to wait out its full 60s send timeout. It now receives an immediate "browser session is closed" error, which client stop paths already handle.

Measured (SIGKILL Firefox mid-recording, then bro.stop()): close latency 60,054ms → 41ms, and the recording zip delivers with the trace and an honest video/index.json. The MCP surface gets the same probe before its finalize. Unit tests cover the offline-finalize paths (partial video embeds, empty engine file recorded as an error rather than embedded, bytes-only cleanup); full make test green (5m41s).

Closes #316.

closeSession queued behind any in-flight recording command, so a
wedged command against a dead browser held teardown for the full
command timeout (#316). The issue's proposed fix — abort all pending
commands first — would have broken auto-finalize-on-close, which needs
the connection to deliver an active recording.

Probe instead: when the browser's state is unknown, one cheap getTree
bounded to two seconds decides the path. Alive → finalize over the
connection, then tear down, exactly as before. Dead → abort pending
commands first (the mutex frees immediately) and finalize offline:
package the trace from memory and embed the engine's live-muxed video
when it is readable and non-empty, with the manifest recording why the
video is cut short. When the routing goroutine triggers the close it
already watched the connection die, so it skips the probe entirely.

Measured on a SIGKILLed Firefox mid-recording: close went from 60.1s
to 41ms, and the recording delivers instead of vanishing. The 60s was
mostly a second bug this surfaced: a client command racing session
deletion was dropped with no reply, leaving the client to wait out its
full send timeout — it now gets an immediate "browser session is
closed" error. The MCP surface gets the same probe before its
finalize.

Closes #316.
@hugs
hugs merged commit fa780e4 into main Aug 9, 2026
1 check passed
@hugs
hugs deleted the fix/316-close-probe branch August 9, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

closeSession can stall behind a wedged screencast command

1 participant