anomalyco / anomalyco/opencode
macOS: exit/cleanup path broadcasts SIGTERM to ALL user processes (kill -1) — kills Ghostty, Terminal, Chrome, entire session (1.18.24 & 1.18.25)
@nexxeln is already working on this.
Since Aug 28, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
On macOS, opencode's exit/cleanup path broadcasts SIGTERM to every process the user owns — the equivalent of kill(-1, SIGTERM). Quitting opencode (or killing a stuck one) took down my entire session every time: Ghostty, Terminal.app, Google Chrome, and every user-level agent, all at once. There are no crash reports for the killed apps because they die from signal 15.
This happened repeatedly today on two exit paths:
- Normal exit / SIGTERM to a running
opencode runprocess → immediate broadcast. - Orphaned children: SIGKILL the main opencode process and a surviving child process (also named
opencode) fires the same broadcast ~1 minute later.
Confirmed on both 1.18.24 and 1.18.25 (downgrading did not help), installed via the official install script.
Environment
- macOS 26.5.2 (25F84), Apple Silicon (Mac15,7)
- opencode 1.18.24 and 1.18.25,
~/.opencode/bin/opencodeviahttps://opencode.ai/install
Evidence
The kernel's sandbox logs the broadcast when it denies the protected (system) targets. Unprotected user processes (terminals, browser) are killed successfully. From log show, at the moment an opencode process exited:
kernel (Sandbox) System Policy: opencode(37536) deny(1) signal initproc signum:15
kernel (Sandbox) System Policy: opencode(37536) deny(1) signal same-sandbox [xprotectd(389)] signum:15
kernel (Sandbox) System Policy: opencode(37536) deny(1) signal others [systemstatusd(448)] signum:15
kernel (Sandbox) System Policy: opencode(37536) deny(1) signal same-sandbox [suhelperd(576)] signum:15
signal initproc = it attempted to signal launchd (pid 1), which only happens with a kill-everything call. Three separate incidents logged today (08:21, 08:32, 09:06 local), the last one from 1.18.24.
To isolate it, I ran opencode inside a sandbox-exec profile that denies signals outside its own process tree, then sent it a plain SIGTERM. The kernel then logged opencode attempting to SIGTERM every process on the system, one by one:
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [AirPlayXPCHelper(419)] signum:15
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [KernelEventAgent(411)] signum:15
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [mds(378)] signum:15
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [powerd(363)] signum:15
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [thermalmonitord(387)] signum:15
kernel (Sandbox) Sandbox: opencode-real(65034) deny(1) signal others [bluetoothd(415)] signum:15
... (continues for every pid on the box)
A canary process with a TERM trap, running outside the sandbox, received nothing — confirming the sandbox was the only thing standing between opencode's exit and the rest of the session.
Steps to reproduce
- macOS:
opencode run "reply with exactly: OK"(or open the TUI). - Send the process SIGTERM (or just let it exit), with other apps open.
- Watch every user-owned process receive SIGTERM. To observe it non-destructively, wrap opencode in a
sandbox-execprofile ((version 1)(allow default)(deny signal)(allow signal (target same-sandbox))) and watch the kernel denials inlog show.
Expected behavior
On exit, opencode should signal only its own children / process group — never -1. This looks like a cleanup call of the form process.kill(-pid, 'SIGTERM') where pid ends up as 1 (e.g. using getppid() after the parent died and the child was reparented to launchd), or an equivalent kill(-1).
Possibly related (terminal dies on exit, Windows): #35327, #22003, #36671.
Workaround
Rename the real binary and front it with a signal-guard wrapper:
- macOS:
sandbox-exec -f guard.sb ~/.opencode/bin/opencode-real "$@"with the profile above. - Linux:
bwrap --dev-bind / / --proc /proc --unshare-pid --die-with-parent ~/.opencode/bin/opencode-real "$@".
With the guard in place opencode works normally, and its exit broadcast hits nothing outside its own tree.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.