Skip to content

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

Open

Description

@g-taki

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

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    on Apr 11, 2026
  2. etraut-openai commented on Apr 11, 2026

    @etraut-openai
    Contributor

    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?

  3. g-taki commented on Apr 12, 2026

    @g-taki
    Author

    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 codex → hangs

    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.

  4. iwinux commented on Apr 13, 2026

    @iwinux
    Image
  5. iwinux commented on Apr 14, 2026

    @iwinux

    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 :)

  6. cuber commented on Apr 22, 2026

    @cuber

    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.

  7. iwinux commented on Apr 23, 2026

    @iwinux

    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.
  8. ashwch commented on Apr 27, 2026

    @ashwch

    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:

      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:

      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.

  9. ashwch commented on May 6, 2026

    @ashwch

    (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 #17447 (comment) — 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.

    2. 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.

  10. cossio commented on May 9, 2026

    @cossio

    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.

  11. andersongmxde commented on May 15, 2026

    @andersongmxde

    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: cp → codesign --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.

  12. jensenojs commented on Jun 9, 2026

    @jensenojs

    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.

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

    CLIIssues related to the Codex CLIbugSomething isn't workingperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions