Skip to content

fix(cli show): handle EEXIST in singleton race#40779

Open
Skn0tt wants to merge 1 commit into
microsoft:mainfrom
Skn0tt:cli-show-pid-handshake
Open

fix(cli show): handle EEXIST in singleton race#40779
Skn0tt wants to merge 1 commit into
microsoft:mainfrom
Skn0tt:cli-show-pid-handshake

Conversation

@Skn0tt
Copy link
Copy Markdown
Member

@Skn0tt Skn0tt commented May 11, 2026

While adding a test for the singleton behaviour, I found that sometimes the loser gets EEXIST. It's hard to reproduce, most runs hit EADDRINUSE because the winner's listen() settles before the loser arrives. You can trigger it by removing the "bindTitle" or, alternatively, by inserting await new Promise(r => setTimeout(r, 250)) right before server.listen(). It'll reliably trigger the following error in one of 5 runs:

### Dashboard opened with pid 97705.
Error: listen EEXIST: file already exists /var/folders/g0/.../dashboard/app.sock
    at Server.setupListenHandle [as _listen2] (node:net:1918:21)
    at listenInCluster (node:net:1997:12)
    at Server.listen (node:net:2119:5)
    at acquireSingleton (.../coreBundle.js:69674:16)
    at async Object.openDashboardApp (.../coreBundle.js:69725:22)

The losing side of the singleton handshake now reads back the holder's
process pid from the socket, so `### Dashboard opened with pid <N>` is
always the long-lived dashboard pid (not the short-lived spawned child
that exited after losing).

Also accept `EEXIST` (in addition to `EADDRINUSE`) as the "someone else
got there first" signal from `server.listen()`. Two cli children
arriving at `bind(2)` in lockstep can produce either error code from
libuv depending on timing; treat both as a cue to retry-via-connect.
@Skn0tt Skn0tt changed the title fix(cli show): pid handshake + handle EEXIST in singleton race fix(cli show): handle EEXIST in singleton race May 11, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Test results for "MCP"

16 failed
❌ [chrome] › mcp/annotate.spec.ts:330 › should annotate when context has no fixed viewport @mcp-windows-latest-chrome
❌ [chrome] › mcp/annotate.spec.ts:398 › should cancel browser_annotate when the MCP client disconnects @mcp-windows-latest-chrome
❌ [chrome] › mcp/annotate.spec.ts:427 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chrome
❌ [chrome] › mcp/annotate.spec.ts:476 › should disengage annotate mode when --annotate client disconnects @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-devtools.spec.ts:217 › video-start-stop @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-devtools.spec.ts:231 › video-chapter @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-json.spec.ts:177 › close-all after open returns closed sessions @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:44 › close named session @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:56 › close-all @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:70 › delete-data @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:82 › delete-data named session @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:99 › session stops when browser exits @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:113 › session reopen with different config @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:130 › workspace isolation - sessions in different workspaces are isolated @mcp-windows-latest-chrome
❌ [chrome] › mcp/cli-session.spec.ts:162 › list --all lists sessions from all workspaces @mcp-windows-latest-chrome
❌ [chrome] › mcp/dashboard.spec.ts:182 › two concurrent cli show invocations both succeed @mcp-windows-latest-chrome

7054 passed, 1068 skipped


Merge workflow run.

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.

1 participant