macOS Seatbelt sandbox blocks os.cpus() — missing mach-host permission breaks yarn/npm

Resolved 💬 2 comments Opened Feb 9, 2026 by kaladivo Closed Feb 12, 2026

What version of Codex CLI is running?

0.98.0

What subscription do you have?

pro

Which model were you using?

gpt-5.3-codex

What platform is your computer?

Darwin 25.2.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

Ghostty, VScode

What issue are you seeing?

os.cpus() returns an empty array when commands run under the macOS Seatbelt sandbox. This causes yarn, npm, node-gyp, and other tools that use os.cpus().length for worker parallelism to fail or produce incorrect behavior.

Root Cause

Node.js os.cpus() calls libuv's uv_cpu_info(), which on macOS uses the Mach API host_processor_info(). The current seatbelt policy does not grant (allow mach-host*), so this call silently fails and returns an empty array.

The policy does allow sysctl-read for hw.ncpu, hw.activecpu, etc., so os.availableParallelism() likely works — but many packages still rely on os.cpus().length.

What steps can reproduce the bug?

# With default sandbox (workspace-write):
codex --sandbox workspace-write

# Ask codex to run:
node -e "const os=require('os');console.log('cpus',os.cpus().length);console.log('availableParallelism',os.availableParallelism?.())"

Expected: cpus prints the actual CPU count (e.g. 10)
Actual: cpus 0 - yarn install and other tools break

What is the expected behavior?

See above

Additional information

Suggested Fix

Add (allow mach-host*) to the seatbelt base policy in codex-rs/core/src/seatbelt.rs, similar to the existing (allow process-info* (target same-sandbox)) rule. This is a read-only query with no security implications.

Workaround

Monkey-patch os.cpus via NODE_OPTIONS:

# ~/.codex/config.toml
[shell_environment_policy.set]
NODE_OPTIONS = "--require /path/to/fix-cpus.js"
// fix-cpus.js
const os = require('os');
const original = os.cpus;
os.cpus = function () {
  const result = original.call(this);
  if (!result || result.length === 0) {
    const count = os.availableParallelism?.() || 4;
    return Array.from({ length: count }, () => ({
      model: 'unknown', speed: 0,
      times: { user: 0, nice: 0, sys: 0, idle: 0, irq: 0 },
    }));
  }
  return result;
};

Interesting is, that this works in codex app. I am seeing this bug only in codex CLI....

  • #7099 (seatbelt blocks hw.optional.neon sysctl, breaking Qt builds)
  • #3987 (seatbelt policy fix for Java)
  • nodejs/node#38190 (os.cpus() returns empty array on restricted platforms)
  • nodejs/node-gyp#3191 (build fails when cpus().length === 0)

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗