stablyai / stablyai/orca

[Bug]: macOS — startup PATH probe hangs (unkillable, requires reboot) under Endpoint Security agents (Jamf Protect / CrowdStrike) due to spawn-heavy interactive shell init

Open
#5,657 5 comments 1 reaction 1 assignee Claimed by @OrcaWin View on GitHub
bug os:macos pending_repro size/m
Dominant language
TypeScript
Stars
71.3k
Forks
4.7k
Avg merge
14h 54m
Merged PRs (30d)
520

Description

### Operating system

macOS

### Orca version

1.4.77

### Details

**Summary**

On macOS, Orca hangs indefinitely at launch (no window, dock icon shows running but is unkillable) on machines running an Endpoint Security (ES) agent such as Jamf Protect or CrowdStrike Falcon. The hang is caused by Orca's startup interactive-shell PATH probe.

**Root cause**

At startup Orca discovers the interactive `PATH` by spawning the user's login shell as `/bin/zsh -i -l -c '... __ORCA_SHELL_PATH__ ...'` (run through a node-pty, so stdout is a TTY). Because it's an interactive login shell, this evaluates the user's full `.zshrc`, which commonly forks subprocesses (oh-my-zsh, `compinit`, `source <(tool completion zsh)`, nvm, sdkman, etc.).

On a managed Mac, every `exec`/`posix_spawn` must be authorized by each active ES extension via `ES_EVENT_TYPE_AUTH_EXEC` before the kernel lets it proceed. When the probe forks several nested subprocesses, those `AUTH_EXEC` verdicts do not return promptly, and the spawn blocks **in the kernel** uninterruptibly.

`sample` of the hung process shows the main thread parked in `posix_spawn`:

```
2589 main-thread
... v8 / node ...
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
...
posix_spawn (in libsystem_kernel.dylib)
__posix_spawn (in libsystem_kernel.dylib) <- blocked
```

`ps` shows both the Orca process and its `zsh` probe child in state `U` (uninterruptible sleep):

```
PID STAT COMMAND
82059 ?E (Orca)
82060 U /bin/zsh -ilc printf '%s' '__ORCA_SHELL_PATH__'; printf '%s' "$PATH"; ...
```

Because the child is wedged in `U`, neither `kill -9`, Activity Monitor "Force Quit", nor `killall` can reap it — the only recovery is a reboot. Active ES extensions confirmed via `systemextensionsctl list`: `com.jamf.protect.security-extension`, `com.crowdstrike.falcon.Agent`.

This is the same shell-probe subsystem behind #4879 (agents show "Not installed" because the GUI PATH differs from the interactive PATH) — Orca relies on running the full interactive shell to learn PATH.

**Why it's intermittent / not widely reported**

Plain users without an ES agent never see a stall because exec authorization is effectively instant. Only managed/corporate Macs (Jamf Protect, CrowdStrike, etc.) expose it, where the spawn-heavy interactive init turns an authorization delay into a permanent kernel wedge.

**Suggested fixes (Orca side)**

1. **Add a timeout to the PATH probe** and fall back to a sane default PATH (or a non-interactive `zsh -lc` result) if it doesn't return within, say, 2-3s. A slow/blocked `AUTH_EXEC` verdict should never be able to hang app launch indefinitely.
2. **Probe PATH without a full interactive shell** where possible — e.g. `zsh -lc 'printf %s "$PATH"'` (login, non-interactive) or read `path_helper` + known version-manager dirs, avoiding `compinit`/completion/plugin subprocess spawns entirely.
3. Run the probe off the main thread / fully async so the Electron main run loop is never blocked on it.

**Workaround (user side)**

Guard the spawn-heavy, interactive-only parts of `.zshrc` so they only run in a real terminal session, not during the `-c` probe. `ZSH_EXECUTION_STRING` is set only for `zsh -c` invocations (the probe) and empty for an interactive session, so it's a reliable discriminator even under a PTY:

```zsh
# Run interactive-only, subprocess-spawning init only in a real terminal,
# not during a GUI app's `zsh -ilc` PATH probe (which wedges under
# Endpoint Security agents because each nested exec stalls AUTH_EXEC).
if [[ -z $ZSH_EXECUTION_STRING ]]; then _real_tty=1; else _real_tty=0; fi

(( _real_tty )) && source $ZSH/oh-my-zsh.sh
(( _real_tty )) && autoload -Uz compinit && compinit && source <(entire completion zsh)
# ... guard nvm / sdkman / completion generators likewise ...
```

PATH/env exports stay unguarded, so the probe still returns a complete PATH — instantly (probe dropped from ~1.7s to ~0.08s and, crucially, spawns nothing). After applying this, Orca launches normally.

### Steps to reproduce

1. On a macOS machine with an Endpoint Security agent active (e.g. Jamf Protect or CrowdStrike Falcon — verify with `systemextensionsctl list`).
2. Use a `.zshrc` that spawns subprocesses at interactive startup (oh-my-zsh, `compinit`, `source <(tool completion zsh)`, nvm, sdkman, etc.).
3. Launch Orca from the Dock/Finder.
4. Orca hangs with no window; `ps` shows the Orca process and a `/bin/zsh -ilc ... __ORCA_SHELL_PATH__ ...` child stuck in state `U`; the process cannot be killed and requires a reboot.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.