Skip to content

[Bug]: playwright-cli open fails with EINVAL when session name is long on macOS due to unix socket path length #40878

@eai04191

Description

@eai04191

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.

  1. npm install -g @playwright/cli
  2. 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

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions