Skip to content

[Bug] v0.0.70: outputDir regression - tries to mkdir at filesystem root / on macOS #1509

@Gatescrispy

Description

@Gatescrispy

Bug Description

After upgrading from @playwright/mcp@0.0.60 to @playwright/mcp@0.0.70 (via @latest), the MCP server fails with:

Error: ENOENT: no such file or directory, mkdir '/.playwright-mcp'

This happens on macOS where / is a read-only filesystem (SIP/APFS sealed system volume). The navigate, click, and other state-mutating tools fail, while snapshot and run_code work since they don't trigger outputFile().

Root Cause

The outputDir() function was refactored between v0.0.60 and v0.0.70, removing the fallback to os.tmpdir().

v0.0.60 (playwright/lib/mcp/browser/config.js:293) — has proper fallback:

function outputDir(config, clientInfo) {
  const rootPath = firstRootPath(clientInfo);
  return config.outputDir 
    ?? (rootPath ? path.join(rootPath, ".playwright-mcp") : void 0) 
    ?? path.join(tmpDir(), String(clientInfo.timestamp));
  //   ↑ safe fallback to tmpdir
}

v0.0.70 (playwright-core/lib/tools/backend/context.js:272) — no fallback:

function outputDir(options) {
  if (options.config.outputDir)
    return path.resolve(options.config.outputDir);
  return path.resolve(options.cwd, ".playwright-mcp");
  //                   ↑ options.cwd is "/" when launched as MCP server without explicit CWD
}

When the MCP server is spawned by an IDE (e.g. Windsurf/VS Code) without an explicit cwd, options.cwd resolves to /, leading to mkdir('/.playwright-mcp') which fails on macOS.

Expected Behavior

outputDir() should fall back to os.tmpdir() or os.homedir() when the CWD-based path is not writable, as it did in v0.0.60.

Environment

  • OS: macOS 15 (Sequoia) — APFS sealed system volume, / is read-only
  • Node: v22.x
  • Package: @playwright/mcp@0.0.70 (latest as of April 2, 2026)
  • Working version: @playwright/mcp@0.0.60
  • IDE: Windsurf (Codeium)
  • MCP config:
{
  "args": ["-y", "@playwright/mcp@latest"],
  "command": "npx",
  "env": { "HOME": "/Users/username" }
}

Workaround

Pin to @playwright/mcp@0.0.60 in the MCP config.

Suggested Fix

Restore the fallback chain in outputDir():

function outputDir(options) {
  if (options.config.outputDir)
    return path.resolve(options.config.outputDir);
  const cwdPath = path.resolve(options.cwd, options.config.skillMode ? ".playwright-cli" : ".playwright-mcp");
  // Fallback if CWD is root or not writable
  if (options.cwd === '/' || options.cwd === '')
    return path.join(os.tmpdir(), 'playwright-mcp-output');
  return cwdPath;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions