Skip to content

Playwright Chromium fails in Codex Desktop macOS sandbox with MachPortRendezvousServer permission denied #21292

Open

Description

@charleswilke

What version of Codex is running?

  • Codex CLI: 0.116.0
  • Codex Desktop app: unknown from the agent session

What platform is your computer?

  • macOS 26.4.1 (25E253)
  • Apple Silicon / arm64
  • Node.js v25.8.1
  • Playwright 1.59.1

What issue are you seeing?

When Codex Desktop runs a local Playwright Chromium launch inside the default sandboxed command/runtime path, Chromium crashes during startup with a macOS Mach bootstrap permission error:

FATAL:base/apple/mach_port_rendezvous_mac.cc:159
Check failed: kr == KERN_SUCCESS.
bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100)

This prevents browser QA / screenshot smoke tests from running through sandboxed shell commands and through a Node REPL MCP/tool runtime. The same Playwright script works when run through Codex with escalated permissions.

Steps to reproduce

From a Codex Desktop session on macOS, in a local project that has Playwright installed:

node -e '(async()=>{const {chromium}=require("playwright");const browser=await chromium.launch({headless:true});const page=await browser.newPage();await page.goto("data:text/html,<title>ok</title>");console.log(await page.title());await browser.close();})().catch(e=>{console.error(String(e.message||e).split("\n").slice(0,10).join("\n"));process.exit(1);});'

Observed in the normal sandboxed command path: Chromium launches, then immediately exits/crashes with the MachPortRendezvousServer Permission denied (1100) error above.

Also reproduced via a Node REPL MCP/tool runtime using:

const { chromium } = await import('playwright');
const browser = await chromium.launch({ headless: true });

What did you expect to happen?

Codex should either:

  • allow Chromium/Playwright to start successfully in an approved browser/test context, or
  • detect this macOS sandbox failure and prompt for escalation with a clear message, or
  • route browser automation through a Codex-supported browser tool path.

What happened instead?

Chromium fails inside the sandbox with:

[err] FATAL:base/apple/mach_port_rendezvous_mac.cc:159
bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100)

In some full Chrome for Testing launch variants, Playwright reports the browser closed and then has trouble killing the process from inside the sandbox:

exception while trying to kill process: Error: kill EPERM

Additional testing

These variants were tried and did not resolve the sandboxed failure:

  • default Playwright Chromium headless shell
  • full chromium.executablePath() / Chrome for Testing
  • channel: 'chromium'
  • args: ['--disable-features=MachPortRendezvous']
  • args: ['--disable-features=DialMediaRouteProvider,MediaRouter']

The same kind of Playwright launch succeeds when Codex runs the command with escalated permissions outside the sandbox.

Workaround

Use an escalated shell command for Playwright browser QA, or avoid launching Chromium from sandboxed tool runtimes such as the Node REPL MCP. A more durable workaround is to create a scoped script like npm run browser:smoke and approve that command prefix.

Why this matters

Browser-game/frontend verification flows in Codex Desktop often need Playwright for screenshots, console checks, and canvas/WebGL smoke tests. On macOS, the default sandbox currently makes that workflow fail in a way that looks like a Chromium/Playwright crash rather than an actionable Codex permission boundary.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    sandboxIssues related to permissions or sandboxing
    tool-callsIssues related to tool calling
    on May 6, 2026
  2. github-actions commented on May 6, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  3. wlccgp3 commented on May 27, 2026

    @wlccgp3

    Root Cause

    Chromium's multi-process architecture relies on registering a global Mach port for IPC between the main process and its child processes (renderer, GPU, utility). At startup, the main process calls bootstrap_check_in() to register:

    org.chromium.Chromium.MachPortRendezvousServer.<PID>
    

    This is a global Mach port name, not a local one. The current Seatbelt base policy (seatbelt_base_policy.sbpl) only allows local Mach port registration — it does not include any (allow mach-register ...) rule for global names matching org.chromium.*.

    When this registration is denied by the kernel sandbox, Chromium hits KERN_FAILURE and crashes with:

    FATAL:base/apple/mach_port_rendezvous_mac.cc:159
    Check failed: kr == KERN_SUCCESS.
    bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100)
    

    No Chromium flag (--no-sandbox, --disable-features=MachPortRendezvous, etc.) can work around this — it's an OS-level denial that happens before Chromium's own sandbox initialization.

    Fix

    Add a single rule to codex-rs/sandboxing/src/seatbelt_base_policy.sbpl:

    ;; Allow Chromium/Electron multi-process IPC (Playwright, Puppeteer, headless browser automation)
    (allow mach-register (global-name-regex #"^org\.chromium\."))

    This precisely scopes the allow to Chromium's namespace without opening up arbitrary global Mach port registration. The regex anchors to ^org\.chromium\. so only Chromium-family processes benefit.

    Additionally, these supplementary rules reduce non-fatal but noisy denies from headless Chromium:

    ;; Core Animation render server (headless still initializes GPU compositing path)
    (allow mach-lookup (global-name "com.apple.CARenderServer"))
    
    ;; DNS resolution config (without this, DNS works but logs warnings)
    (allow mach-lookup (global-name "com.apple.SystemConfiguration.DNSConfiguration"))
    
    ;; Memory pressure monitoring
    (allow sysctl-read (sysctl-name "kern.memorystatus_vm_pressure_level"))
    
    ;; Hardware identification (ARM feature detection)
    (allow sysctl-read (sysctl-name "hw.product"))
    (allow sysctl-read (sysctl-name "hw.optional.neon_fp16"))
    (allow sysctl-read (sysctl-name "hw.optional.neon_hpfp"))

    Context

    Headless Chromium (via Playwright/Puppeteer) is a common workflow for AI coding agents — screenshot verification, frontend smoke tests, browser-visible behavior checks. This is the same pattern used by Claude Code's /verify and browser review workflows, and by Codex's own Computer Use feature.

    I maintain a Seatbelt sandbox wrapper for Claude Code that reuses Codex's base .sbpl policies. Adding the mach-register rule above resolved the issue completely — Playwright headless runs inside the sandbox without any escalation prompt.

    Happy to open a PR if this direction looks right.

  4. longtran1904 commented on Jul 20, 2026

    @longtran1904

    Thanks @wlccgp3 for the solution.
    I ran to the exact same issue.

    I'm using Playwright version 1.61.1
    I also had to add this rule for it to work
    (allow mach-lookup (global-name-regex #"^org\.chromium\."))

  5. tonydehnke commented on Aug 23, 2026

    @tonydehnke

    Reproduced this exact failure on 2026-08-22 from Codex Desktop on Apple Silicon/macOS while profiling a Rails 8.1/Minitest suite whose system tests use Capybara + Playwright-managed Chromium.

    The sandboxed full-suite command reached the browser population and repeatedly produced:

    Playwright::TargetClosedError: Target page, context or browser has been closed
    exception while trying to kill process: Error: kill EPERM
    process did exit: exitCode=null, signal=SIGABRT
    

    Playwright was launching its worktree-managed chromium-1234 full build in headless mode (the executable bundle is named Google Chrome for Testing.app). Because the suite retries browser failures, the sandbox denial caused repeated launch/abort attempts and visible macOS crash dialogs. The run ended with 15 environmental browser errors, while the equivalent browser population passes on the same project's Apple CI runner outside the Codex seatbelt.

    The additional impact for repository test runners is that a normal full-suite command can execute all non-browser tests but return invalid/partial browser evidence that looks like an application regression. Early detection plus an actionable narrow-escalation prompt—or routing a repository-owned browser runner through a scoped approval—would prevent both the misleading result and repeated crash dialogs. Broad sandbox disabling is not an acceptable workaround for this workflow.

  6. clemens commented on Sep 29, 2026

    @clemens

    (Disclaimer: AI-generated, but verified by me.)

    Additional reproduction on macOS 26.7.1 (25G241), Apple Silicon, with Playwright 1.63.0.

    The active task uses a custom permission profile with filesystem read-deny rules. Chromium headless shell fails with:

        bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<PID>:
        Permission denied (1100)
    

    Retrying through the tool with sandbox_permissions: "require_escalated" produces the same denial. I cannot confirm that the retry actually ran outside Seatbelt.

    Full Chrome for Testing and Firefox also abort during _RegisterApplication. Running the same offline synthetic test harness from normal macOS Terminal succeeds: all four tests pass across Chromium and Firefox.

    The discussion in #46405 suggests a possible explanation: approved escalation can retain the sandbox to preserve denied-read restrictions. This may also affect browser launches; I have not verified that implementation path in the active Desktop runtime.

    Could maintainers investigate browser launch under a custom profile with effective read-deny rules, including the approved retry? A diagnostic explaining when escalation remains sandboxed, and a supported way to allow the required browser IPC while preserving filesystem denies, would help.

  7. mike0528 commented on Oct 10, 2026

    @mike0528

    Additional reproduction on current Codex Desktop (2026-10-10):

    • App: 26.1007.21159 (build 20052); bundled CLI: 0.162.0-alpha.17.2; macOS 27.0.1, arm64; Node 24.19.0; Playwright 1.62.1.
    • A minimal chromium.launch({ headless: true }) run in the default command sandbox fails before navigating to about:blank with bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100), followed by SIGTRAP. This was reproduced after updating the desktop app.
    • The in-product /feedback flow reported successful submission. Feedback ID: 01a11a52-36ca-7013-bed3-27e63908883d.

    Could maintainers confirm whether this report is tracked by this issue and whether a scoped sandbox fix or supported Playwright workflow is planned? The documented built-in Browser loopback preview can render local work, but it does not allow this default-sandbox Playwright launch path.

  8. pych-ky commented on Oct 13, 2026

    @pych-ky

    Additional confirmation on Codex CLI/TUI 0.162.1 (macOS/arm64), with effective filesystem read-deny rules:

    • A minimal Playwright Chromium launch fails with the same MachPortRendezvousServer error in both normal and approved require_escalated execution.
    • A separate read-only sandbox_check(mach-register) probe returned denied in both modes; the approved probe's parent returned allowed.
    • In this exact release, denied reads select EscalatedSandboxWithRestrictions. The matching base policy is also embedded in the installed binary.

    This supports the deny-preserving escalation explanation above. The sandbox_check probe only queried policy; no deny-removal or patched-build test was performed.

  9. luketraas commented on Oct 14, 2026

    @luketraas

    Same failure here with the Codex CLI's app-server (codex-cli 0.162.1, workspace-write, network on), not only Codex Desktop. Measured on macOS, Playwright 1.63.0, Chrome Headless Shell 153.0.8010.12, captured with codex sandbox --log-denials:

    1. Plain chromium.launch({ headless: true }): exits about 90 ms in with bootstrap_check_in … org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100), SIGTRAP. The denial log shows mach-register org.chromium.Chromium.MachPortRendezvousServer.<pid>.
    2. Adding --in-process-gpu: same crash, because the renderer and network service still launch as child processes.
    3. Adding --in-process-gpu --single-process: works. Page, DOM check and screenshot all succeed, and the screenshot is byte-identical to an unsandboxed control. Chromium registers the rendezvous service on its first child-process launch, so the denial only goes away when no child processes are launched. Single-process mode is unsupported by Chromium and drops renderer isolation, so it is not a real workaround for test suites.

    --no-sandbox (already set by Playwright) and Chrome Headless Shell do not avoid it. Codex's config and permission profiles have no setting that allows a Mach registration, so users cannot work around it locally.

    A narrow rule in codex-rs/sandboxing/src/seatbelt_base_policy.sbpl, along the lines proposed above, would fix it:

    (allow mach-register (global-name-regex #"^org\.chromium\.Chromium\.MachPortRendezvousServer\.[0-9]+$"))
    

    This only allows Chromium to register its own per-process rendezvous name. It is narrower than allowing all of org.chromium.*. Opt-in through a permission profile would work just as well for us.

  10. aquilesr23 commented on Oct 23, 2026

    @aquilesr23

    Additional reproduction with a strong sandbox-vs-host control (2026-10-23):

    • macOS 27.0.1 (26A434), Apple Silicon.
    • In ChatGPT Work / Codex Desktop, Playwright Chromium fails before loading the page with the same error reported here:
      bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.<pid>: Permission denied (1100).
    • In the same Work/Codex environment, Playwright WebKit also aborts at process startup (SIGABRT) in HIServices/AppKit/NSApplicationMain. I am reporting this as an additional sandbox-related observation, not asserting it has the exact same root cause as Chromium.
    • Without changing the app under test, Playwright installation, browser binaries, caches, or dependencies, I ran the exact candidate/test harness from a normal macOS Terminal outside Work/Codex:
      • Chromium: 1,407 / 1,407 checks PASS across 1280, 390, and 320 px.
      • WebKit: 1,407 / 1,407 checks PASS across the same three widths.
      • Total: 2,814 / 2,814 checks PASS outside the Work/Codex sandbox.
    • Re-running from Work/Codex remained blocked before any functional browser assertion executed.

    This makes the environment boundary unusually clear: the same Playwright installation and same test candidate are healthy outside the Codex/Work sandbox, while browser startup is blocked inside it. No reinstall/update/cache cleanup occurred between the failing sandboxed runs and the successful Terminal runs.

    This also means the sandbox failure can create misleading product-test results unless the harness distinguishes “browser could not start” from an application regression.

    Happy to provide sanitized macOS crash excerpts if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingsandboxIssues related to permissions or sandboxingtool-callsIssues related to tool calling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions