microsoft / microsoft/playwright

[Bug]: Bundled Firefox shares the real Firefox app-data dir (~/Library/Application Support/Firefox), which macOS 27 TCC-protects — every launch hangs

Open
#42,768 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
96.3k
Forks
6.5k
Avg merge
1d 6h
Merged PRs (30d)
180

Description

### Summary

Playwright's bundled Firefox resolves its **app-data directory** to `~/Library/Application Support/Firefox` — the same directory the real Firefox uses. On macOS 27 that directory is TCC-protected, so on any machine with Firefox installed, every `firefox.launch()` hangs until it times out unless the *host* process (terminal, editor, CI agent) has Full Disk Access.

Chromium is immune because Playwright gives it `--user-data-dir=`, which is its complete state root. Firefox's `-profile` only sets the profile; Gecko still separately resolves the shared app-data root for `profiles.ini` / `installs.ini` bookkeeping — and that lookup is what gets denied.

This is **not** the same as #42082 (closed as upstream). That report's key unexplained detail — *"stock Nightly double-clicked from Finder does launch — it's the command-line launch with a fresh profile that fails"* — is precisely what TCC predicts: launched from Finder, Firefox is its own responsible process and may access its own protected directory; launched from a terminal, the terminal is the responsible process and is denied.

### Environment

```
Playwright: 1.58.2 (firefox-1509 / Firefox 146.0.1)
and: 1.62.1 (firefox-1538 / Firefox 153.0) <- both reproduce identically
OS: macOS 27.0 (26A428), Apple Silicon arm64
```

### Actual behaviour

```
launch: Timeout 180000ms exceeded.
Call log:
- .../firefox-1509/firefox/Nightly.app/Contents/MacOS/firefox -no-remote -headless -profile /var/folders/.../playwright_firefoxdev_profile-XXXX -juggler-pipe -silent
- pid=55386
- [err] *** You are running in headless mode.
- [err] sandbox_extension_issue_file_to_process failed for .../plugin-container.app: 1 (Operation not permitted)
- [out] Crash Annotation GraphicsCriticalError: RenderCompositorSWGL failed mapping default framebuffer, no dt
```

The temp profile directory is created but stays **completely empty**. Headed, Firefox puts up a modal **"Profile Missing — Your Firefox profile cannot be loaded. It may be missing or inaccessible."** Headless the modal cannot render, so the process spins on the main thread (`sample` shows `SLEventSourceCounterForEventType` → `CGSEventSourceForID` → `CGSScoreboard` in SkyLight) burning CPU until Playwright's timeout.

### The two log lines above are red herrings

`sandbox_extension_issue_file_to_process ... Operation not permitted` is emitted by **working** Gecko browsers on the same machine. Zen (a Firefox fork, Developer ID signed) prints the identical line and starts fine, creating a full 25-file profile. The `RenderCompositorSWGL` line likewise appears on successful startups. Diagnosing from these two lines is what led #42082 to the wrong conclusion.

### Root cause, with proof

The app-data directory is TCC-protected — permissive mode, correct owner, still denied:

```
$ ls -ld ~/Library/Application\ Support/Firefox
drwxr-xr-x@ 8 user staff 256 ... /Users/user/Library/Application Support/Firefox

$ ls ~/Library/Application\ Support/Firefox/
ls: ...: Operation not permitted

$ touch ~/Library/Application\ Support/Firefox/.probe
touch: ...: Operation not permitted
```

No filesystem flags, no ACL, no `com.apple.quarantine`. Scanning `~/Library/Application Support`, the denied entries are Apple's privacy directories plus exactly two browsers — both installed, both protected:

```
DENIED Firefox, Microsoft Edge
OK Google, Chromium, BraveSoftware, Vivaldi, Arc, com.operasoftware.Opera
```

**Proof it is the app-data directory and nothing else.** Playwright's `Contents/Resources/application.ini` is branded:

```ini
[App]
Vendor=Mozilla
Name=Firefox
```

Pointing the same binary at an identical `application.ini` with only `Name`/`Vendor` changed makes it start **immediately**:

```
$ firefox -app .../Contents/Resources/browser/application.ini \
-no-remote -headless -profile /tmp/p -juggler-pipe -silent
Juggler listening to the pipe # <- starts
$ ls /tmp/p | wc -l
20 # <- profile written
```

(`-app` must be the *first* argument, so this is not expressible through `launchOptions.args`.)

**Proof it is not Playwright's build or its code signature.** Stock `/Applications/Firefox.app` (Developer ID, `org.mozilla.firefox`) fails the same way from the command line, with Playwright not involved:

```
$ /Applications/Firefox.app/Contents/MacOS/firefox -no-remote -headless -profile /tmp/realfx -silent
*** You are running in headless mode.
Could not find profile folder.
```

Also ruled out: re-signing Playwright's bundle so `codesign --verify --deep --strict` passes (still fails); relocating the browser out of `~/Library/Caches` (still fails); `XRE_PROFILE_PATH`, `MOZ_LEGACY_PROFILES`, `MOZ_DISABLE_*_SANDBOX` (no effect).

### Why this is actionable for Playwright

Even though the denial originates in macOS, Playwright is uniquely positioned to be unaffected. The bundled browser is already branded `CodeName=Nightly` and is not a user-facing Firefox install — it has no reason to share the real Firefox's app-data directory. Giving the bundled build its **own** `Name`/`Vendor` (e.g. `PlaywrightFirefox`) would make it self-contained in the same way `--user-data-dir` makes Chromium self-contained, and it would sidestep this class of OS-level protection permanently.

Verified above: with a distinct `Name`/`Vendor`, the exact same binary launches and writes its profile with **no permissions granted at all**.

### Impact

On macOS 27, any developer or CI machine with Firefox installed loses all Playwright Firefox runs. The only workarounds today are granting Full Disk Access to every host application individually (terminal, editor, CI agent — I confirmed Terminal.app and an editor fail independently, each needing its own grant), or uninstalling Firefox.

### Repro

```js
const { firefox } = require('playwright');
// On macOS 27 with /Applications/Firefox.app installed,
// run from a terminal that does NOT have Full Disk Access:
(async () => { await firefox.launch(); })(); // hangs until timeout
```

Happy to test a patched build on macOS 27 — I have a reliable reproduction and the diagnosis above was done on that machine.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the bundled Firefox Contents/Resources/application.ini and the provided firefox.launch() reproduction on macOS 27; inspect how its Name and Vendor values are set. Done means the bundled browser launches and creates its temporary profile without requiring Full Disk Access when the real Firefox app is installed.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
67/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.