Repository navigation
Missing SIGHUP handler causes orphaned processes to leak ~4.7 GB each after terminal death #14504
Description
Bug Report
Summary
When a controlling terminal is destroyed (SSH disconnect, tmux pane close, terminal emulator crash), opencode processes become orphaned and continue running indefinitely, each consuming ~4.7 GB of RAM. This is because opencode has no SIGHUP handler, and the TUI event loop blocks on stdin that will never produce input, preventing process.exit() in the finally block of index.ts from ever executing.
Observed Impact
On a system running opencode via oh-my-opencode with tmux, 14 orphaned opencode processes accumulated over time, consuming ~66 GB of RAM total. All had PPID=1 (adopted by init), no controlling terminal, and no alive children.
Root Cause
In packages/opencode/src/index.ts, the main flow is:
try {
await cli.parse() // blocks on TUI promise → stdin
} catch (e) { ... } finally {
process.exit() // only reached when cli.parse() resolves
}When the TUI command runs (thread.ts), cli.parse() awaits the TUI promise (line 185), which waits for interactive terminal input. When the terminal dies:
- Kernel sends
SIGHUPto the process group - No SIGHUP handler exists → signal is ignored by the Bun event loop
stdinis now a dead file descriptor but the TUI event loop doesn't detect thiscli.parse()never resolves →process.exit()never runs- Process continues consuming memory indefinitely as an orphan (
PPID=1)
Environment
- opencode v1.2.10
- Bun 1.3.6
- Linux (Arch)
- oh-my-opencode v3.7.4 (manages tmux sessions)
Reproduction
- Start opencode in a tmux pane
- Kill the tmux pane or session externally (e.g.
tmux kill-pane) - Observe the opencode process continues running with
PPID=1 - Repeat — each orphaned process retains ~4.7 GB
Proposed Fix
Add signal handlers before the main try block in index.ts:
for (const sig of ["SIGHUP", "SIGTERM", "SIGPIPE"] as const) {
process.on(sig, () => {
process.exit(1)
})
}- SIGHUP: Terminal death (SSH disconnect, tmux pane close)
- SIGTERM: Graceful shutdown requests
- SIGPIPE: Broken pipe when output destination is gone
This ensures process.exit() is called immediately on terminal death, which triggers the existing subprocess cleanup logic and prevents orphaned processes.
Additional Context
The existing comment on lines 198-200 of index.ts already acknowledges that subprocesses can hang:
Some subprocesses don't react properly to SIGTERM and similar signals. Most notably, some docker-container-based MCP servers don't handle such signals unless run using
docker run --init.
The same class of problem affects the parent opencode process itself — it doesn't react to SIGHUP at all.
Investigation Notes
- Bun Workers are threads (via
std.Thread.spawn()inweb_worker.zig), not processes — they are not the source of zombie child processes - The orphaned processes were full opencode instances that lost their controlling terminal
- Bun's process reaping (
WaiterThreadinprocess.zig) only handles children spawned viaBun.spawn(), not the parent process lifecycle
Activity
github-actions commentedon Feb 20, 2026
This issue might be a duplicate of existing issues. Please check:
Please review these issues before continuing, and consider closing this one as a duplicate if they cover the same problem.
fix: add SIGHUP/SIGTERM/SIGPIPE handlers to prevent orphaned processes
fix: handle SIGHUP/SIGTERM/SIGPIPE to prevent orphaned processes
fix(opencode): add cwd-scoped process kill on exit for Linux/macOS
2026-02-26: Fix OpenCode Process Cleanup on Linux/macOS (#93)
OmerFarukOruc commentedon Feb 27, 2026
If you're hitting this on Linux — I wrote a cleanup script that finds and kills orphaned opencode processes (no TTY). Quick workaround until it's fixed.
Usage:
oc-cleanup(list),oc-cleanup kill(kill orphans),oc-cleanup watch(monitor all)Full gist + README: https://gist.github.com/OmerFarukOruc/d1bf2bb1f6f4f01c3a0e63288ada7c94
See also: #15348
WietRob commentedon Mar 2, 2026
Confirming this issue with additional evidence from a different system.
Environment
Memory Evidence (smaps_rollup)
Active terminal process:
Orphaned process (TTY "?"):
Key Findings
Memory is 99.9% Private_Dirty heap - not file cache, not LSP children. This is opencode itself allocating unbounded memory.
Process tree confirms location:
→ The leak is in the parent process, not MCP/LSP subprocesses.
Orphaned process survives terminal - matches SIGHUP handler theory.
Additional Context
This strongly supports the SIGHUP handler fix proposed in this issue. The process receives SIGHUP on terminal disconnect but the handler doesn't trigger cleanup/exit, leaving orphaned processes that continue leaking heap memory.
Happy to test any patches or provide additional diagnostics.
WietRob commentedon Mar 2, 2026
Configurable Memory Manager
I've created an enhanced version of the cleanup script that addresses the multi-instance use case mentioned in this thread.
Key Features
~/.config/opencode/memory.confWhy This Matters
The original issue describes memory leaks growing to 60GB+. With multiple opencode windows:
Installation
Configuration
Edit
~/.config/opencode/memory.conf:Usage
This tool has prevented several OOM kills on my system (93GB RAM, usually 7-10 opencode instances running).
Happy to share the full script if there's interest in adding it to the project.
thdxr commentedon Apr 2, 2026
Closing in favor of #20695 where we're collecting all memory-related problems. Please follow that issue for updates.