What's going on?
playwright-cli intentionally starts cliDaemon.js detached so a browser session survives individual CLI commands. That is useful, but there is currently no owner lease or idle policy. If the daemon's control socket directory disappears, the daemon and its Chromium tree remain alive indefinitely while every normal CLI lifecycle command reports that the browser is not open.
This is different from #41664. That issue was about MCP stdio disposal; its concrete long-lived process was later identified as cliDaemon.js, and the maintainer response correctly noted that a CLI daemon is expected to remain open until the session is closed. The gap here is that after control-socket loss, the session can no longer be closed through the CLI at all.
Standalone reproduction
Run this in a disposable environment with a uniquely named session:
SOCKET_ROOT="$(mktemp -d)"
PWTEST_SOCKETS_DIR="$SOCKET_ROOT" \
playwright-cli -s=owner-lease-repro open about:blank
# Simulate an ephemeral owner/temp directory disappearing.
mv "$SOCKET_ROOT" "$SOCKET_ROOT.removed"
PWTEST_SOCKETS_DIR="$SOCKET_ROOT" \
playwright-cli -s=owner-lease-repro snapshot
# Browser 'owner-lease-repro' is not open / run open first
# The detached daemon is still alive and still owns its browser tree.
ps auxww | grep '[c]liDaemon.js owner-lease-repro'
The .session metadata still names the old socket path, but Session._connect() cannot reach it. The daemon has no idle timeout or owner-liveness signal, so it cannot self-terminate. playwright-cli -s=owner-lease-repro close follows the same dead socket and cannot reclaim it.
Observed impact
On one long-running macOS automation host I measured:
- 19
cliDaemon processes reparented to PID 1
- oldest daemon: 4 days 8 hours
- only 5 still reachable through the CLI registry/socket path
- 14 had a
TMPDIR that no longer existed
- ~14.8 GB resident memory across daemon/Chromium trees
- approximately 10.7 GB belonged to daemons no live automation session claimed
- median ~0.81 GB per tree
Requested lifecycle option
Could the CLI daemon support an opt-in owner lifecycle, for example one of:
--idle-timeout=<duration> / an equivalent environment or config option;
- an owner lease/heartbeat channel — when the lease disappears or expires, the daemon stops itself;
- a parent-death/owner-watch mode that is cross-platform and does not require a consumer to rediscover and kill the daemon by numeric PID later.
Desired safety semantics:
- a CLI-launched browser/context is closed when the opted-in owner lease expires;
- an attached operator browser is disconnected/detached, not closed;
- explicitly persistent sessions can opt out or use a different timeout;
- ordinary default behavior can remain unchanged for compatibility.
Why downstream cleanup is insufficient
A consumer can keep PWTEST_SOCKETS_DIR outside ephemeral scratch, which preserves manual close reachability and is a useful mitigation. It cannot safely implement complete crash-orphan reclamation after the fact on all platforms:
- same-UID filesystem ownership records can be rewritten by the automation process;
- multiple managers can share one host/data home;
- numeric PID/process-group identities can be reused;
- Linux has pidfds, but macOS has no equivalent identity handle that remains pinned through an external group signal.
A daemon-owned lease or timeout avoids these cross-process authority problems entirely.
Downstream context
Contribution intent
I intend to work on this if maintainers agree that an owner lease or idle policy belongs in Playwright CLI and approve/assign it for community contribution. I will wait for maintainer triage before opening a PR. Once approved, I can add the cross-platform regression tests and implement the agreed lifecycle API.
Version
@playwright/cli 0.1.18
- Node.js 24.19.0
- macOS arm64
What's going on?
playwright-cliintentionally startscliDaemon.jsdetached so a browser session survives individual CLI commands. That is useful, but there is currently no owner lease or idle policy. If the daemon's control socket directory disappears, the daemon and its Chromium tree remain alive indefinitely while every normal CLI lifecycle command reports that the browser is not open.This is different from #41664. That issue was about MCP stdio disposal; its concrete long-lived process was later identified as
cliDaemon.js, and the maintainer response correctly noted that a CLI daemon is expected to remain open until the session is closed. The gap here is that after control-socket loss, the session can no longer be closed through the CLI at all.Standalone reproduction
Run this in a disposable environment with a uniquely named session:
The
.sessionmetadata still names the old socket path, butSession._connect()cannot reach it. The daemon has no idle timeout or owner-liveness signal, so it cannot self-terminate.playwright-cli -s=owner-lease-repro closefollows the same dead socket and cannot reclaim it.Observed impact
On one long-running macOS automation host I measured:
cliDaemonprocesses reparented to PID 1TMPDIRthat no longer existedRequested lifecycle option
Could the CLI daemon support an opt-in owner lifecycle, for example one of:
--idle-timeout=<duration>/ an equivalent environment or config option;Desired safety semantics:
Why downstream cleanup is insufficient
A consumer can keep
PWTEST_SOCKETS_DIRoutside ephemeral scratch, which preserves manualclosereachability and is a useful mitigation. It cannot safely implement complete crash-orphan reclamation after the fact on all platforms:A daemon-owned lease or timeout avoids these cross-process authority problems entirely.
Downstream context
Contribution intent
I intend to work on this if maintainers agree that an owner lease or idle policy belongs in Playwright CLI and approve/assign it for community contribution. I will wait for maintainer triage before opening a PR. Once approved, I can add the cross-platform regression tests and implement the agreed lifecycle API.
Version
@playwright/cli0.1.18