macOS Seatbelt sandbox blocks os.cpus() — missing mach-host permission breaks yarn/npm
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.neonsysctl, 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)
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗