Codex CLI v0.120.0 (Rust) hangs at _dyld_start on macOS 26.4.1 Tahoe — Gatekeeper/AppleSystemPolicy blocks Homebrew Cask binary

Open 💬 11 comments Opened Apr 11, 2026 by g-taki
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.
Update (2026-04-12): Original diagnosis was incorrect — the issue is not about code signing format but about missing Apple notarization + macOS 26's persistent quarantine database. Not version-specific (both v0.119.0 and v0.120.0 work when downloaded directly). See my follow-up comment for revised root cause and fix.

What version of Codex is running?

codex-cli 0.120.0 (Rust rewrite, installed via Homebrew Cask)

What subscription do you have?

Plus

Which model were you using?

N/A — binary never launches

What platform is your computer?

macOS 26.4.1 Tahoe (Build 25E253), Mac mini, Apple Silicon

What issue are you seeing?

The Codex CLI binary installed via brew install --cask codex hangs indefinitely on launch. The process gets stuck at _dyld_start — the dynamic linker never loads a single library. No output, no error, no Gatekeeper dialog (accessed via SSH).

Diagnosis:

  • sample shows 100% of samples stuck at _dyld_start (in dyld) + 0
  • DYLD_PRINT_LIBRARIES=1 produces zero output — no libraries loaded
  • spctl --assess --type execute returns: rejected (the code is valid but does not seem to be an app)
  • com.apple.provenance xattr is present (SIP-protected, cannot be removed)
  • The binary is validly signed but macOS AppleSystemPolicy silently kills it before dyld completes

Workaround:

Copying the binary to a new location strips com.apple.provenance, then ad-hoc re-signing makes it work:

cp /opt/homebrew/Caskroom/codex/0.120.0/codex-aarch64-apple-darwin /tmp/codex-clean
codesign --force --sign - /tmp/codex-clean
/tmp/codex-clean --version  # works: codex-cli 0.120.0

The original binary at the Caskroom path never works regardless of xattr -d com.apple.quarantine or codesign --force -s - in place.

What steps can reproduce the bug?

  1. brew install --cask codex on macOS 26.4.1 Tahoe (25E253)
  2. codex --help — hangs indefinitely, no output
  3. Over SSH, no Gatekeeper dialog appears; the process is silently blocked

What is the expected behavior?

The binary should launch normally after Homebrew installation.

Additional information

  • This is the same class of issue as #5787 (quarantine/provenance on older versions) and is related to #17225 / #16767 (AppleSystemPolicy blocking Codex processes).
  • #17339 reports a similar dyld start crash on the same OS build (25E253) for the Desktop app.
  • Other tools have hit the same macOS 26 Gatekeeper enforcement: anthropics/claude-code#20407, astral-sh/uv#16726 (fixed via post-build ad-hoc codesigning in CI).
  • The uv project resolved this by adding codesign --force -s - to their release pipeline (PR astral-sh/uv#17123). A similar fix in the Codex Rust build CI would resolve this permanently.

View original on GitHub ↗

11 Comments

etraut-openai contributor · 3 months ago

Thanks for the bug report. Has this worked for you in previous version? I'm trying to determine if this was a regression in 0.120.0 specifically. If you install 0.119.0, does that work for you?

g-taki · 3 months ago

Thanks for looking into this, @etraut-openai.

I did further testing today and the results change the diagnosis significantly:

Version testing:

  • v0.119.0 downloaded directly from GitHub releases → works
  • v0.120.0 downloaded directly from GitHub releases → also works
  • v0.120.0 installed via brew install --cask codexhangs

So this is not a regression between versions. Both work fine when downloaded directly.

Root cause (revised):

The issue is the interaction between Homebrew Cask and macOS 26's Gatekeeper:

  1. Homebrew Cask downloads the tarball and macOS sets com.apple.quarantine on the extracted binary
  2. spctl --assess --type execute returns rejected — the binary is properly signed (Developer ID) but not notarized
  3. On macOS 26, the kernel blocks execution of quarantined + unnotarized binaries before dyld even starts
  4. Crucially, xattr -d com.apple.quarantine does not fix it — macOS 26 persists the quarantine state in a system-level database (not just the xattr)
  5. Copying the binary to a new path (new inode) bypasses the quarantine DB and works immediately — no re-signing needed
# This works without any re-signing:
cp /opt/homebrew/Caskroom/codex/0.120.0/codex-aarch64-apple-darwin /tmp/codex
/tmp/codex --version  # codex-cli 0.120.0

Suggested fix:

Submit the release binaries to Apple's notary service (xcrun notarytool submit). Once notarized, Gatekeeper will allow execution even with quarantine. This would fix Homebrew Cask installs on macOS 26 permanently.

Workaround for users:

Use the official install script instead of Homebrew Cask — it downloads directly to ~/.local/bin/ without triggering Homebrew's quarantine:

curl -fsSL https://github.com/openai/codex/releases/download/rust-v0.120.0/install.sh | sh

I'll also correct my original issue description — the earlier hypothesis about "obsolete code signature format" was wrong. The signing is fine; the missing piece is notarization.

iwinux · 3 months ago

<img width="372" height="374" alt="Image" src="https://github.com/user-attachments/assets/b7804ace-adca-4b72-8bb0-4a41002818f9" />

iwinux · 3 months ago

Interesting. I re-install it via Homebrew today (the same v0.120.0) and it runs without error. Assuming you haven't silently replaced the binary, it might be a glitch at the Apple side?

BTW: you should warn users NEVER run xattr -d com.apple.quarantine when such issue happens :)

cuber · 2 months ago

Confirming this still reproduces on codex-cli 0.122.0 / macOS 26 Tahoe (Darwin 25.3) / Apple Silicon, installed via brew install --cask codex.

One correction to the workaround: cp does not strip com.apple.provenance on my system — the xattr is still present on the copied file at /tmp. What actually fixes the hang is the ad-hoc resign alone:

cp /opt/homebrew/Caskroom/codex/0.122.0/codex-aarch64-apple-darwin /tmp/codex-clean
codesign --force --sign - /tmp/codex-clean
mv /tmp/codex-clean /opt/homebrew/Caskroom/codex/0.122.0/codex-aarch64-apple-darwin

After this, codex --version works at the original Caskroom path even though com.apple.provenance is still on the inode. So the trigger seems to be the combination of the original signature + provenance, not the xattr's presence.

iwinux · 2 months ago

Here comes the the weird part: it's not 100% reproducible, even with the same macOS version + Codex CLI version:

  • Device A: "could not verify ..." error
  • Device B: runs just fine
  • codex binary installed via brew install --cask codex has the same SHA256.
ashwch · 2 months ago

Thanks for the detailed write-up. cc @iwinux since this appears to match your report/workaround closely.

I hit what looks like the same failure mode on codex-cli 0.125.0, installed via Homebrew Cask, on macOS 26.4.1 / Darwin 25.4.0 arm64.

Symptoms:

  • A codex exec --json ... process appeared to hang before producing any output.
  • A fresh /opt/homebrew/bin/codex --version also hung.
  • sample <pid> 3 showed every sample at _dyld_start; no useful Codex/Rust frames.
  • vmmap -summary <pid> reported:

``text
Process exists but has not started -- it is launched-suspended
``

  • The process had only a tiny memory footprint, no CPU activity, and no child process, so it was not blocked inside Codex agent work.
  • codesign --verify --strict --verbose=2 passed for the executable.
  • spctl --assess --type execute -vv reported:

``text
rejected (the code is valid but does not seem to be an app)
origin=Developer ID Application: OpenAI OpCo, LLC (2DC432GLL2)
``

Quarantine details:

  • The cask executable initially had com.apple.quarantine with Homebrew Cask in the value.
  • After clearing quarantine on the executable, the hang remained.
  • xattr -lr /opt/homebrew/Caskroom/codex/0.125.0 then showed quarantine still present on the cask version directory.
  • After xattr -dr com.apple.quarantine /opt/homebrew/Caskroom/codex/0.125.0, the original executable still launched suspended.

The strongest datapoint: a byte-identical copy of the same executable ran normally.

cp /opt/homebrew/Caskroom/codex/0.125.0/codex-aarch64-apple-darwin /tmp/codex-test
/tmp/codex-test --version
# codex-cli 0.125.0

cmp -s /opt/homebrew/Caskroom/codex/0.125.0/codex-aarch64-apple-darwin /tmp/codex-test
# identical

Replacing the original cask executable inode with the byte-identical working copy fixed the Homebrew path:

cp /opt/homebrew/Caskroom/codex/0.125.0/codex-aarch64-apple-darwin ./codex-copy
./codex-copy --version
# codex-cli 0.125.0

mv -f ./codex-copy ./codex-aarch64-apple-darwin
/opt/homebrew/bin/codex --version
# codex-cli 0.125.0

codesign --verify --strict --verbose=2 /opt/homebrew/Caskroom/codex/0.125.0/codex-aarch64-apple-darwin
# valid on disk
# satisfies its Designated Requirement

So in this case it was not a Codex runtime hang. The process was created but never resumed before dyld completed. Clearing visible com.apple.quarantine was insufficient; creating a new inode for the same signed bytes fixed it. This seems consistent with macOS 26.4.1 / AppleSystemPolicy retaining launch-blocking state for the original Homebrew Cask artifact outside the normal visible xattr path.

ashwch · 2 months ago

(Previous comment removed — posted prematurely before the full fix was identified. This is the corrected version.)

---

macOS 26.4.1 (25E253), Darwin 25.4.0, ARM64 — Codex v0.128.0 via Homebrew Cask — two separate issues, both triggered by the Cask install path

Following up on https://github.com/openai/codex/issues/17447#issuecomment-4329004287 — the inode-replacement fix alone was not sufficient here. The full fix required both ad-hoc re-signing and moving the binary out of the Caskroom directory tree.

Symptoms (identical to OP)

  • Any codex command hangs immediately, zero output, 0% CPU
  • sample <pid>: every sample at _dyld_start
  • vmmap -summary <pid>: "Process exists but has not started -- it is launched-suspended"
  • spctl --assess --type execute -vv: rejected (the code is valid but does not seem to be an app)

What was tried, in order

| # | Attempt | Result | Why |
|---|---|---|---|
| 1 | xattr -d com.apple.quarantine on the binary | Still hung | Block is not stored in visible xattrs |
| 2 | xattr -rd com.apple.quarantine on the entire Caskroom tree | Still hung | Same |
| 3 | cp binary to /tmp (fresh inode, clean path) | Still hung | Block had escalated to CDHash level — all copies of those signed bytes blocked system-wide |
| 4 | cp + rm + mv to force new inode at original path | Still hung | Same CDHash = same cached block |
| 5 | brew reinstall --cask codex | Still hung | Re-downloads identical signed bytes |
| 6 | codesign --remove-signature | Killed with SIGKILL (exit 137) — no output at all, even --version | macOS 26.4.1 kills unsigned arm64 Mach-O binaries outright |
| 7 | codesign -s - (ad-hoc sign) + run from /tmp | Works from /tmp ✅ | Ad-hoc signature avoids both the Developer ID heuristic AND the unsigned-binary kill |
| 8 | Copy the ad-hoc signed binary into the Caskroom path | Hangs again ❌ | The Caskroom directory itself had com.apple.quarantine, and binaries placed inside it inherited the launch-suspended behavior |
| 9 | Place ad-hoc signed binary directly in /opt/homebrew/bin/ + symlink | Works ✅ | Bypasses the quarantined directory entirely |

What actually fixed it

# 1. Copy binary out of the quarantined Caskroom directory
cp /opt/homebrew/Caskroom/codex/0.128.0/codex-aarch64-apple-darwin \
   /opt/homebrew/bin/codex-aarch64-apple-darwin

# 2. Ad-hoc sign (avoids Developer ID heuristic + avoids unsigned-binary kill)
codesign -s - /opt/homebrew/bin/codex-aarch64-apple-darwin
xattr -c /opt/homebrew/bin/codex-aarch64-apple-darwin

# 3. Point symlink to the clean location
rm -f /opt/homebrew/bin/codex
ln -sf /opt/homebrew/bin/codex-aarch64-apple-darwin /opt/homebrew/bin/codex

# 4. Clean the Caskroom dir quarantine so it doesn't poison future files
xattr -d com.apple.quarantine /opt/homebrew/Caskroom/codex/0.128.0

After this, codex --version prints codex-cli 0.128.0 and exits 0 normally.

Two distinct mechanisms at play

  1. CDHash-level launch-suspension — AppleSystemPolicy caches a deny decision keyed on the binary's code directory hash. Once the Developer ID-signed binary has been blocked, all copies of those bytes are launch-suspended system-wide, regardless of path or inode. This is why brew reinstall (downloading the same signed bytes) doesn't help.
  1. Quarantined directory poisoning — The com.apple.quarantine xattr on the Caskroom directory (/opt/homebrew/Caskroom/codex/0.128.0/) propagates to any executable placed inside it, re-triggering the launch-suspension even for binaries that work fine from /tmp or /opt/homebrew/bin/.

The earlier inode-replacement workaround likely worked because (a) the block hadn't yet escalated to CDHash-level on that machine and (b) the directory quarantine was cleared first. By the time a machine hits exit-137-on-unsigned behavior, both issues need to be addressed.

cossio · 2 months ago

Reproduces on Codex 0.130.0 — and the SSH/remote case makes it much worse than a typical Gatekeeper prompt

Adding a data point that highlights why this is more severe than a one-time consent dialog:

  • macOS 26.4.1 (25E253) / Darwin 25.4.0 / Apple Silicon
  • codex-cli 0.130.0 via brew install --cask codex — same _dyld_start hang, Process exists but has not started -- it is launched-suspended, signed by OpenAI OpCo, LLC (2DC432GLL2) but not notarization-stapled (xcrun stapler validate → "does not have a ticket stapled to it").
  • Triggered by a routine brew upgrade, not a fresh install. Every Cask update re-applies com.apple.quarantine and re-arms the prompt, so this recurs on every release.
  • The host is headless / SSH-only. The Gatekeeper consent dialog ("codex-aarch64-apple-darwin" is an app downloaded from the Internet. Are you sure you want to open it?) is rendered on the local console, where there's no human to dismiss it. From the SSH side, codex simply hangs forever with no output, no error, and no indication that a UI prompt is the blocker. Diagnosing it requires sample <pid> (everything at _dyld_start) plus log show --predicate 'process == "syspolicyd"' to even realize Gatekeeper is involved.
  • In my case the only way to find out was connecting via Screen Sharing and seeing the dialog already open on the desktop.

This effectively bricks codex on every CI runner, build server, and remote-dev box on macOS 26 until someone physically (or via remote desktop) clicks "Open" — ssh alone cannot recover the machine.

The proposed fix earlier in this thread — notarizing release binaries with xcrun notarytool submit — would resolve this cleanly: notarized + stapled binaries don't trigger the first-launch consent dialog, so headless installs via Homebrew Cask would just work.

andersongmxde · 2 months ago

First, thanks to @g-taki for the meticulous original diagnosis and the revised root-cause writeup, to @cuber for the cleaner in-place resign variant, to @ashwch for the comprehensive workaround that actually unblocked me, to @cossio for the most recent confirmation on 0.130.0, and to @etraut-openai for engaging on triage. This thread is the single best resource on the bug.

Still reproducing on Codex CLI 0.130.0 / macOS 26.4.1 Tahoe (build 25E253) as of 2026-05-15 — and on this machine the simpler in-place resign workaround was not sufficient.

Posting an additional data point because the workaround selection seems to be machine-dependent, and the OS-level signature here is identical to what @g-taki and @ashwch already reported. Hopefully this helps anyone (or any AI coding assistant) landing on this issue while trying to figure out why codex hangs with no output on macOS Tahoe.

Environment

  • Mac mini, Apple Silicon (ARM64), macOS 26.4.1 Tahoe, build 25E253, Darwin 25.4.0
  • codex-cli 0.130.0 installed via brew install --cask codex
  • Signed by OpenAI OpCo, LLC (2DC432GLL2) (Developer ID), xcrun stapler validate reports no notarization ticket
  • Headless usage over SSH (driven by Claude Code), Gatekeeper consent dialog was not visible on the local console

Symptoms (matching the OP exactly)

  • Every codex invocation hung indefinitely. codex --version, codex --help, even codex completion zsh and codex completion fish (spawned automatically by shell startup) all hung. Six unkillable codex-aarch64-apple-darwin processes had silently accumulated through the day — every new terminal that loaded zsh/fish completions spawned another doomed instance.
  • ps -axo pid,stat,wchan,etime,pcpu,command: state S, WCHAN empty, 0% CPU, 41+ minutes elapsed
  • sample <pid> 2 -mayDie: 100% of samples at _dyld_start (in dyld) + 0, physical footprint stuck at 112K, peak 112K (i.e. dyld never loaded a single library)
  • lsof -p <pid>: zero TCP sockets, only the codex binary mapped + dyld + redirected stdio. No network reached, no config loaded.
  • vmmap -summary <pid>: "Process exists but has not started -- it is launched-suspended" (matches @ashwch's report)
  • spctl --assess --type execute -vv on the binary: rejected (the code is valid but does not seem to be an app)
  • SIGTERM ignored. SIGKILL eventually landed but took several seconds — consistent with the process being suspended in a mach-port wait for an AppleSystemPolicy verdict that never came.

If you're an LLM-assisted developer searching for codex hangs, codex no output, codex --version hangs forever, codex stuck at dyld start macOS Tahoe, brew install cask codex broken, AppleSystemPolicy launched-suspended, OpenAI codex SIGKILL macOS 26, homebrew codex notarization, or codex completion zsh hangs — yes, this is the issue, and the comments below contain the fix.

Workaround comparison on this machine

| Approach | Result | Notes |
|---|---|---|
| xattr -d com.apple.quarantine on the binary | Still hung | Quarantine state is persisted at the system policy DB level, not just the xattr (matches @g-taki's revised finding) |
| @cuber's in-place fix: cpcodesign --force -s -mv back to Caskroom | Still hung | Adhoc signature was confirmed (Signature=adhoc, new Identifier codex-clean-<sha1>), xattr cleared, but codex --version still timed out at 15s. |
| @ashwch's full fix: move binary out of Caskroom to /opt/homebrew/bin/, adhoc-sign there, repoint the codex symlink, clear Caskroom dir quarantine | Works. codex --version returns codex-cli 0.130.0 in ~1.4s, exit 0. |

This is consistent with @ashwch's "CDHash-level deny escalation" theory: once AppleSystemPolicy / syspolicyd / amfid has cached enough deny decisions for the Developer-ID-signed bytes, the deny is keyed on the code directory hash and propagates to any path that contains those bytes. Only an ad-hoc resign (which produces a fresh CDHash) AND a path outside any quarantined directory clears both layers.

Drop-in script for the full fix

For anyone who needs to recover quickly:

# Run AFTER `brew install --cask codex` or `brew upgrade codex` (re-run on every upgrade)
set -e
CODEX_BIN=$(ls /opt/homebrew/Caskroom/codex/*/codex-aarch64-apple-darwin | head -1)
cp "$CODEX_BIN" /opt/homebrew/bin/codex-aarch64-apple-darwin
codesign --force -s - /opt/homebrew/bin/codex-aarch64-apple-darwin
xattr -c /opt/homebrew/bin/codex-aarch64-apple-darwin
rm -f /opt/homebrew/bin/codex
ln -sf /opt/homebrew/bin/codex-aarch64-apple-darwin /opt/homebrew/bin/codex
xattr -d com.apple.quarantine /opt/homebrew/Caskroom/codex/*/ 2>/dev/null || true
codex --version  # should print codex-cli <version> and exit 0

This needs to be re-run on every brew upgrade codex because the upgrade replaces the /opt/homebrew/bin/codex symlink to point back into Caskroom and re-quarantines the binary. A Homebrew Cask postflight block in the cask definition would automate this, but the canonical fix is upstream — adding xcrun notarytool submit && xcrun stapler staple to the Codex release pipeline as @g-taki suggested, the same approach astral-sh/uv took in astral-sh/uv#17123.

Incidental debris worth scrubbing (not the cause of the hang, but accumulates because of it)

Independent of the AppleSystemPolicy block, two pieces of state pile up on machines that have hit this repeatedly. They made initial diagnosis noisier because one of them looked superficially like a stale lockfile, which is a classic Rust-CLI hang pattern:

  • ~/.codex/tmp/arg0/codex-arg0*/ and ~/.codex/tmp/path/codex-arg0*/ — codex's per-invocation codex-execve-wrapper and apply_patch shim directories. Codex appears to create them at startup as part of its sandbox setup, but they don't get cleaned up when the process is killed in launch-suspended state. I had six of them, the oldest containing a 2-day-old .lock file.
  • ~/.codex/auth.json.codex-auth.YYYYMMDD-HHMMSS — codex backs up auth on every run but never deletes the backups. 12 files had accumulated over ~4 days of failed launches.

To clean up safely (auth.json itself is preserved):

rm -rf ~/.codex/tmp/
mkdir -p /tmp/codex-auth-backups && mv ~/.codex/auth.json.codex-auth.* /tmp/codex-auth-backups/

To be explicit so future readers don't go down the wrong path: the .lock file is not the cause of the hang. The hang is the OS-level AppleSystemPolicy block this issue describes. The lockfile is just an artifact left behind by previously-hung invocations.

Thanks again to everyone in this thread — it is genuinely one of the more useful OSS bug investigations I have come across. Happy to share additional diagnostic output (full sample traces, log show --predicate 'process == "syspolicyd"' excerpts, etc.) if that would help on the OpenAI side.

jensenojs · 1 month ago

Adding a current data point from macOS arm64 after the rust-v0.138.0 release.

This is notable because 0.138.0 includes #26252 (ci: sign macOS release artifacts with Azure Key Vault), but the GitHub release artifact still fails local strict code-signing assessment on this machine.

Environment:

  • macOS: 26.5.1 build 25F80, arm64
  • Codex Desktop currently installed: 26.602.40724 build 3593
  • Bundled Desktop CLI: codex-cli 0.137.0-alpha.4
  • Tested release artifact: rust-v0.138.0 / codex-aarch64-apple-darwin.tar.gz
  • Artifact URL: https://github.com/openai/codex/releases/download/rust-v0.138.0/codex-aarch64-apple-darwin.tar.gz

What I tested:

curl -L --fail --show-error --output /private/tmp/codex-rust-v0.138.0-aarch64.tar.gz \
  https://github.com/openai/codex/releases/download/rust-v0.138.0/codex-aarch64-apple-darwin.tar.gz

mkdir -p /private/tmp/codex-rust-v0.138.0-aarch64-extract
tar -xzf /private/tmp/codex-rust-v0.138.0-aarch64.tar.gz \
  -C /private/tmp/codex-rust-v0.138.0-aarch64-extract

/private/tmp/codex-rust-v0.138.0-aarch64-extract/codex-aarch64-apple-darwin --version
codesign --verify --deep --strict --verbose=2 /private/tmp/codex-rust-v0.138.0-aarch64-extract/codex-aarch64-apple-darwin
codesign -dv --verbose=4 /private/tmp/codex-rust-v0.138.0-aarch64-extract/codex-aarch64-apple-darwin
spctl -a -vv -t execute /private/tmp/codex-rust-v0.138.0-aarch64-extract/codex-aarch64-apple-darwin
/private/tmp/codex-rust-v0.138.0-aarch64-extract/codex-aarch64-apple-darwin completion zsh

Observed:

codex-cli 0.138.0

completion zsh succeeds and emits completions, but strict signing and Gatekeeper assessment still fail:

codesign --verify:
invalid signature (code or signature have been modified)
In architecture: arm64
codesign -dv:
Authority=(unavailable)
TeamIdentifier=2DC432GLL2
Runtime Version=15.5.0
spctl:
internal error in Code Signing subsystem

So at least on this machine, the 0.138.0 macOS arm64 release artifact is runnable in a direct terminal smoke test, but is still not accepted by strict codesign / spctl. This looks like the signing/notarization pipeline fix may not yet be reflected in the published single-binary artifact, or the artifact is still missing something Gatekeeper expects.