Version
@playwright/cli@0.1.9 (bundling playwright-core@1.60.0-alpha-1777077614000). Source on main (72bbd1d) still has the same construction.
Steps to reproduce
There is no separate repo for this reproducer; the bug is purely platform/path-length, triggered by any session name long enough to push the resulting socket path over 104 bytes on macOS.
npm install -g @playwright/cli
- Run:
playwright-cli -s=awesome-coding-agent-orchestrators open about:blank --persistent
Session name is 34 chars; this is a real-world repository basename used as the session id.
Expected behavior
The session opens. Long session names (e.g. a repo basename used as a stable session id per workspace) should work on macOS.
Actual behavior
The daemon fails to listen() on the unix socket with EINVAL:
node_modules/@playwright/cli/node_modules/playwright-core/lib/tools/cli-client/session.js:167
const rejectWithPid = (reject, message) => reject(Object.assign(new Error(`Daemon pid=${child.pid}: ${message}`), { daemonPid: child.pid }));
^
Error: Daemon pid=81522: listen EINVAL: invalid argument /var/folders/19/k71k_rw96kzb3ryz91_bhw4m0000gn/T/pw-baeaa08c/cli/330a3070819fdca7-awesome-coding-agent-orchestrators.sock
at rejectWithPid (.../session.js:167:69)
at Socket.<anonymous> (.../session.js:177:11)
...
Additional context
Root cause
The CLI daemon embeds the unbounded user-supplied session name in the socket filename:
|
function daemonSocketPath(clientInfo: ClientInfo, sessionName: string): string { |
|
return makeSocketPath('cli', `${clientInfo.workspaceDirHash}-${sessionName}`); |
|
} |
makeSocketPath then concatenates without checking the resulting length:
|
export function makeSocketPath(domain: string, name: string): string { |
|
const userNameHash = calculateSha1(process.env.USERNAME || process.env.USER || 'default').slice(0, 8); |
|
if (process.platform === 'win32') { |
|
const socketsDir = process.env.PLAYWRIGHT_SOCKETS_DIR; |
|
const suffix = socketsDir ? `-${calculateSha1(socketsDir).slice(0, 8)}` : ''; |
|
return `\\\\.\\pipe\\pw-${userNameHash}-${domain}-${name}${suffix}`; |
|
} |
|
const baseDir = process.env.PLAYWRIGHT_SOCKETS_DIR || path.join(os.tmpdir(), `pw-${userNameHash}`); |
|
const dir = path.join(baseDir, domain); |
|
const result = path.join(dir, `${name}.sock`); |
|
fs.mkdirSync(dir, { recursive: true }); |
|
return result; |
|
} |
On macOS, os.tmpdir() returns the per-user $TMPDIR (/var/folders/XX/YYYYYYYYYYY/T/, ~49 chars). Adding pw-<8-char hash>/cli/<16-char workspaceDirHash>-<sessionName>.sock leaves only ~17 chars for the session name before hitting sun_path's 104-byte limit, and listen fails with EINVAL. Repo basenames over ~17 chars are common, so this is easy to hit when scripting "session name = repo basename".
Other makeSocketPath callers (unaffected)
The two non-CLI callers use bounded names, so they don't trip the limit today, but they share the same utility and would benefit from any defensive change:
|
private async _socketPath() { |
|
return makeSocketPath('browser', this._browser.guid.slice(0, 14)); |
|
} |
|
function dashboardSocketPath() { |
|
return makeSocketPath('dashboard', 'app'); |
|
} |
Workaround
Setting PLAYWRIGHT_SOCKETS_DIR to a shorter prefix avoids the issue:
PLAYWRIGHT_SOCKETS_DIR=/tmp/pw playwright-cli -s=awesome-coding-agent-orchestrators open ...
This env var is read by makeSocketPath but does not appear to be documented anywhere user-visible (I only found it after grepping the source).
Suggested fix
daemonSocketPath is the only caller passing unbounded user input, so the natural fix is there: when ${workspaceDirHash}-${sessionName} would push the path over the platform limit, replace sessionName with its hash (or with a truncated ${prefix}-${hash} form). The user-visible session name doesn't need to literally appear in the socket filename — the daemon already keeps the user-facing name in its registry separately.
A defensive length check in makeSocketPath itself (throw a clear error message naming the platform limit) would also help future callers, but isn't strictly required for this bug.
Alternatives I considered but think are worse:
Happy to send a PR if hashing the session name at the daemonSocketPath level is the preferred direction.
Environment
System:
OS: macOS 15.7.4
CPU: (10) arm64 Apple M4
Memory: 95.66 MB / 24.00 GB
Binaries:
Node: 24.11.0 - /Users/eai/.volta/tools/image/node/24.11.0/bin/node
Yarn: 1.22.22 - /Users/eai/.volta/tools/image/yarn/1.22.22/bin/yarn
npm: 11.6.1 - /Users/eai/.volta/tools/image/node/24.11.0/bin/npm
pnpm: 11.0.4 - /opt/homebrew/bin/pnpm
bun: 1.2.20 - /Users/eai/.bun/bin/bun
Deno: 2.1.10 - /Users/eai/.deno/bin/deno
IDEs:
VSCode: 1.117.0 - /opt/homebrew/bin/code
Cursor: 0.50.5 - /usr/local/bin/cursor
Claude Code: 2.1.143 - /Users/eai/.local/bin/claude
Languages:
Bash: 3.2.57 - /bin/bash
Version
@playwright/cli@0.1.9 (bundling playwright-core@1.60.0-alpha-1777077614000). Source on main (72bbd1d) still has the same construction.
Steps to reproduce
There is no separate repo for this reproducer; the bug is purely platform/path-length, triggered by any session name long enough to push the resulting socket path over 104 bytes on macOS.
npm install -g @playwright/cliSession name is 34 chars; this is a real-world repository basename used as the session id.
Expected behavior
The session opens. Long session names (e.g. a repo basename used as a stable session id per workspace) should work on macOS.
Actual behavior
The daemon fails to
listen()on the unix socket withEINVAL:Additional context
Root cause
The CLI daemon embeds the unbounded user-supplied session name in the socket filename:
playwright/packages/playwright-core/src/tools/cli-daemon/daemon.ts
Lines 151 to 153 in 72bbd1d
makeSocketPaththen concatenates without checking the resulting length:playwright/packages/utils/fileUtils.ts
Lines 107 to 119 in 72bbd1d
On macOS,
os.tmpdir()returns the per-user$TMPDIR(/var/folders/XX/YYYYYYYYYYY/T/, ~49 chars). Addingpw-<8-char hash>/cli/<16-char workspaceDirHash>-<sessionName>.sockleaves only ~17 chars for the session name before hittingsun_path's 104-byte limit, andlistenfails withEINVAL. Repo basenames over ~17 chars are common, so this is easy to hit when scripting "session name = repo basename".Other
makeSocketPathcallers (unaffected)The two non-CLI callers use bounded names, so they don't trip the limit today, but they share the same utility and would benefit from any defensive change:
playwright/packages/playwright-core/src/server/browser.ts
Lines 256 to 258 in 72bbd1d
playwright/packages/playwright-core/src/tools/dashboard/dashboardApp.ts
Lines 241 to 243 in 72bbd1d
Workaround
Setting
PLAYWRIGHT_SOCKETS_DIRto a shorter prefix avoids the issue:This env var is read by
makeSocketPathbut does not appear to be documented anywhere user-visible (I only found it after grepping the source).Suggested fix
daemonSocketPathis the only caller passing unbounded user input, so the natural fix is there: when${workspaceDirHash}-${sessionName}would push the path over the platform limit, replacesessionNamewith its hash (or with a truncated${prefix}-${hash}form). The user-visible session name doesn't need to literally appear in the socket filename — the daemon already keeps the user-facing name in its registry separately.A defensive length check in
makeSocketPathitself (throw a clear error message naming the platform limit) would also help future callers, but isn't strictly required for this bug.Alternatives I considered but think are worse:
baseDirto/tmpon macOS — reintroduces the per-user collision risk that The same socket file path is used for different users playwright-cli#287 fixedPLAYWRIGHT_SOCKETS_DIRonly — keeps the foot-gun for default users.Happy to send a PR if hashing the session name at the
daemonSocketPathlevel is the preferred direction.Environment
System: OS: macOS 15.7.4 CPU: (10) arm64 Apple M4 Memory: 95.66 MB / 24.00 GB Binaries: Node: 24.11.0 - /Users/eai/.volta/tools/image/node/24.11.0/bin/node Yarn: 1.22.22 - /Users/eai/.volta/tools/image/yarn/1.22.22/bin/yarn npm: 11.6.1 - /Users/eai/.volta/tools/image/node/24.11.0/bin/npm pnpm: 11.0.4 - /opt/homebrew/bin/pnpm bun: 1.2.20 - /Users/eai/.bun/bin/bun Deno: 2.1.10 - /Users/eai/.deno/bin/deno IDEs: VSCode: 1.117.0 - /opt/homebrew/bin/code Cursor: 0.50.5 - /usr/local/bin/cursor Claude Code: 2.1.143 - /Users/eai/.local/bin/claude Languages: Bash: 3.2.57 - /bin/bash