Skip to content

fix: ensure tab-groups data dir exists before acquiring lock file#11

Open
grigoryosifov wants to merge 1 commit into
pm990320:mainfrom
grigoryosifov:fix/tab-groups-ensure-dir-before-lock
Open

fix: ensure tab-groups data dir exists before acquiring lock file#11
grigoryosifov wants to merge 1 commit into
pm990320:mainfrom
grigoryosifov:fix/tab-groups-ensure-dir-before-lock

Conversation

@grigoryosifov
Copy link
Copy Markdown

@grigoryosifov grigoryosifov commented Apr 16, 2026

Problem

When a user invokes any stateful browser_tab_group / browser_tabs call before ~/.ultimate-playwright-mcp/ has been created (fresh machine or wiped data dir), acquireLock() calls:

```ts
writeFileSync(LOCK_PATH, String(process.pid), { flag: "wx" });
```

which fails with ENOENT because the parent directory is missing. The outer catch then tries readFileSync(LOCK_PATH, ...) which also fails with ENOENT, the loop retries, and depending on host filesystem behavior either takes until maxWaitMs to give up or — in practice on some macOS setups — appears to hang indefinitely under load. From a user's perspective: the first stateful tool call freezes.

Reproducer

  1. rm -rf ~/.ultimate-playwright-mcp/
  2. Start UPM connected to a Chrome instance
  3. Call browser_tab_group action=list (or any action that hits the registry)
  4. Observe the hang

Fix

Call ensureDir() at the top of acquireLock(), exactly as readRegistry() and writeRegistry() already do. One-line addition.

Tested

  • Deleted ~/.ultimate-playwright-mcp/ on a test macOS machine (Chrome 147, macOS 26.2 ARM64)
  • Before fix: browser_tab_group action=list hung; browser_tabs action=list hung similarly
  • After fix: returns immediately with "0 tab groups" / empty tab list

When a user invokes any stateful browser_tab_group / browser_tabs call before
~/.ultimate-playwright-mcp/ has been created (fresh machine or wiped data dir),
acquireLock() calls writeFileSync(LOCK_PATH, ..., { flag: 'wx' }) which fails
with ENOENT because the parent directory is missing. The outer catch then tries
readFileSync(LOCK_PATH, ...) which also fails with ENOENT, the loop retries,
and depending on host filesystem behavior either takes until maxWaitMs to give
up or appears to hang indefinitely. From a user's perspective: the first tool
call freezes.

Fix: call ensureDir() at the top of acquireLock(), same as readRegistry() and
writeRegistry() already do.

Credit to @moltenrooster (Joe Lemay) for identifying the root cause while
setting up ultimate-playwright-mcp on a machine with no prior data dir.
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