[BUG] macOS sandbox blocks Chromium/Playwright launch: bootstrap_check_in denied — no mach-register allowance exists, allowMachLookup cannot help
Summary
Headless Chromium (Playwright, Puppeteer, anything Chromium-based) cannot launch inside the macOS Bash sandbox. Chromium's multi-process launch registers a per-pid Mach bootstrap service so its children can rendezvous with the parent; bootstrap_check_in is a mach-register operation, the Seatbelt profile is deny-by-default, and no setting can allow it: the profile contains no mach-register rule at all, and sandbox.network.allowMachLookup — whose schema docs mention Playwright — only ever emits (allow mach-lookup …) rules.
Error
[FATAL:base/apple/mach_port_rendezvous_mac.cc:159] Check failed: kr == KERN_SUCCESS. bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.98874: Permission denied (1100)
The process is then force-killed and Playwright fails with TargetClosedError: BrowserType.launch: Target page, context or browser has been closed.
Root cause
sandbox-runtime's macOS profile (src/sandbox/macos-sandbox-utils.ts) allows a fixed list of mach-lookup names ("based on Chrome sandbox policy") plus user-specified allowMachLookup entries, but grepping the profile shows no mach-register allowance anywhere. Under (deny default), any process that must register a new bootstrap service name — Chromium's MachPortRendezvousServer.<pid>, crashpad's child_port_handshake — dies at launch. So allowMachLookup fixes the lookup class of failure (e.g. Go TLS needing trustd, #29533) but structurally cannot fix this one.
Reproduction
Inside a sandboxed Bash call on macOS, with Playwright + chromium installed:
python -c "from playwright.sync_api import sync_playwright; p = sync_playwright().start(); p.chromium.launch(headless=True)"
Workarounds (both fully unsandbox the command)
sandbox.excludedCommandsmatching the specific scriptdangerouslyDisableSandbox: trueper call (unavailable whenallowUnsandboxedCommands: false)
Neither preserves filesystem/network isolation for the browser process, which is the part one actually wants sandboxed.
Suggested fix
A scoped registration allowance mirroring allowMachLookup, e.g.:
"sandbox": { "network": { "allowMachRegister": ["org.chromium.*"] } }
emitting (allow mach-register (global-name-prefix "org.chromium.")), so browser automation can run with the rest of the sandbox intact.
Prior reports
- #14881 — identical failure (
bootstrap_check_in … Permission denied (1100)from puppeteer/crashpad), auto-closed as stale with "please file a new issue and reference this one". - #29533 — the mach-lookup flavor of the same deny-default (Go TLS / trustd); addressed by
enableWeakerNetworkIsolation/allowMachLookup, which cannot address registration.
Environment
- macOS Darwin 25.5.0 (arm64)
- Claude Code 2.1.220, sandbox enabled,
allowUnsandboxedCommands: false - Playwright (Python) with chromium_headless_shell-1228
3 Comments
Suggested tags: sandbox + macos
Independent confirmation — same failure, different project/org
Hit this exact issue independently while running Angular's Vitest browser-mode unit tests (headless Chromium via
@vitest/browser-playwright) inside the sandboxed Bash tool on macOS. Wanted to add a data point since this confirms the root cause is structural, not scope-dependent. Also tried allowMachLookup: [\"*\"] (full wildcard): identical failure, ruling out a scoping issue.Environment
2.1.24126.1(build 25B78), arm64vitest4.1.10,@vitest/browser-playwright4.1.10,playwright/playwright-core1.56.1chromium_headless_shell-1194enabled: true,failIfUnavailable: true,allowUnsandboxedCommands: falseError
followed by Playwright's
browserType.launch: Target page, context or browser has been closed, and a secondarykill EPERMwhen Playwright tries to force-kill the already-crashed process (also sandbox-denied, but a symptom, not a separate bug).Workaround used
Same as noted here:
sandbox.excludedCommandsmatching the test invocation runs the whole process tree (test runner → Playwright → Chromium) fully outside the sandbox. Confirmed working, but it fully drops filesystem/network isolation for that command, same caveat as already documented in this thread.Adding this in case it helps: prioritizing a scoped
allowMachRegister(mirroringallowMachLookup's design) would let CI/test tooling that spins up headless Chromium/Playwright run under sandbox without a full opt-out.Same failure here on Claude Code 2.1.282, macOS (Apple Silicon), Playwright's chrome-headless-shell:
bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100)What we tried:
sandbox.network.allowMachLookup: ["org.chromium.*"]: settings load, same failure.sandbox.allowMachLookup: true: loads, no effect.A Seatbelt profile with
(allow mach-register (global-name-regex #"^org\.chromium\."))lets it start, which matches this issue's proposal. We'd use anallowMachRegistersetting as soon as it exists; for now we run the browser outside the Claude Code sandbox, under its own profile.