Memory Leak: Orphaned processes when terminal closes without explicit exit #7555

Open
opened 2026-02-16 18:07:34 -05:00 by yindo · 4 comments
Owner

Originally created by @Her-xanadu on GitHub (Jan 25, 2026).

Originally assigned to: @rekram1-node on GitHub.

Problem

When users close terminal windows without explicitly exiting OpenCode (Ctrl+C), the OpenCode process and all its MCP child processes become orphaned (parent becomes launchd/PID 1) and continue consuming memory indefinitely.

Evidence from a real user system:

=== OpenCode Process Stats ===
      41 OpenCode processes
OpenCode main processes: 2638 MB
MCP child processes: 404 MB
Total: ~3.0 GB wasted memory

=== By TTY ===
  38 ??    # Orphaned processes (no controlling terminal)
   3 active terminals

All 38 orphaned processes had PPID=1 (adopted by launchd), meaning their original terminal was closed but the process didn't exit.

Root Cause

OpenCode doesn't properly handle terminal closure signals (SIGHUP) or doesn't propagate termination to MCP child processes.

Proposed Solution

Option 1: Fix in OpenCode Core (Recommended)

Add proper signal handling in the main process:

// Example for Go codebase
func setupSignalHandling() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
    
    go func() {
        <-sigs
        // Kill all child processes (MCP servers)
        killChildProcesses()
        os.Exit(0)
    }()
}

func killChildProcesses() {
    // Get process group and kill all children
    pgid, err := syscall.Getpgid(os.Getpid())
    if err == nil {
        syscall.Kill(-pgid, syscall.SIGTERM)
        time.Sleep(500 * time.Millisecond)
        syscall.Kill(-pgid, syscall.SIGKILL)
    }
}

Option 2: Wrapper Script Workaround (User-side)

For users experiencing this issue now, a wrapper script can help:

#!/bin/bash
# ~/.local/bin/opencode-wrapper

OPENCODE_BIN="/opt/homebrew/bin/opencode"  # or wherever opencode is installed

cleanup() {
    get_descendants() {
        local parent=$1
        local children=$(pgrep -P $parent 2>/dev/null)
        for child in $children; do
            echo $child
            get_descendants $child
        done
    }
    
    local all_children=$(get_descendants $$)
    
    if [ -n "$all_children" ]; then
        echo $all_children | xargs kill 2>/dev/null
        sleep 0.5
        echo $all_children | xargs kill -9 2>/dev/null
    fi
}

trap cleanup EXIT INT TERM HUP QUIT
exec "$OPENCODE_BIN" "$@"

Installation:

chmod +x ~/.local/bin/opencode-wrapper
echo 'alias opencode="~/.local/bin/opencode-wrapper"' >> ~/.zshrc
source ~/.zshrc

Expected Behavior

  1. When terminal is closed (SIGHUP), OpenCode should terminate gracefully
  2. All MCP child processes should be killed before OpenCode exits
  3. No orphaned processes should remain after terminal closure

Environment

  • macOS (arm64)
  • OpenCode installed via Homebrew
  • Multiple MCP servers configured (ssh-mcp, figma, arxiv, markitdown, chrome-devtools, github)

Impact

  • Memory leak: Each orphaned instance uses 60-90 MB
  • MCP processes: Additional ~10 MB per MCP per instance
  • After a week of usage without manual cleanup: 2-3 GB wasted memory
Originally created by @Her-xanadu on GitHub (Jan 25, 2026). Originally assigned to: @rekram1-node on GitHub. ## Problem When users close terminal windows without explicitly exiting OpenCode (Ctrl+C), the OpenCode process and all its MCP child processes become orphaned (parent becomes launchd/PID 1) and continue consuming memory indefinitely. ### Evidence from a real user system: ``` === OpenCode Process Stats === 41 OpenCode processes OpenCode main processes: 2638 MB MCP child processes: 404 MB Total: ~3.0 GB wasted memory === By TTY === 38 ?? # Orphaned processes (no controlling terminal) 3 active terminals ``` All 38 orphaned processes had `PPID=1` (adopted by launchd), meaning their original terminal was closed but the process didn't exit. ### Root Cause OpenCode doesn't properly handle terminal closure signals (SIGHUP) or doesn't propagate termination to MCP child processes. ## Proposed Solution ### Option 1: Fix in OpenCode Core (Recommended) Add proper signal handling in the main process: ```go // Example for Go codebase func setupSignalHandling() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) go func() { <-sigs // Kill all child processes (MCP servers) killChildProcesses() os.Exit(0) }() } func killChildProcesses() { // Get process group and kill all children pgid, err := syscall.Getpgid(os.Getpid()) if err == nil { syscall.Kill(-pgid, syscall.SIGTERM) time.Sleep(500 * time.Millisecond) syscall.Kill(-pgid, syscall.SIGKILL) } } ``` ### Option 2: Wrapper Script Workaround (User-side) For users experiencing this issue now, a wrapper script can help: ```bash #!/bin/bash # ~/.local/bin/opencode-wrapper OPENCODE_BIN="/opt/homebrew/bin/opencode" # or wherever opencode is installed cleanup() { get_descendants() { local parent=$1 local children=$(pgrep -P $parent 2>/dev/null) for child in $children; do echo $child get_descendants $child done } local all_children=$(get_descendants $$) if [ -n "$all_children" ]; then echo $all_children | xargs kill 2>/dev/null sleep 0.5 echo $all_children | xargs kill -9 2>/dev/null fi } trap cleanup EXIT INT TERM HUP QUIT exec "$OPENCODE_BIN" "$@" ``` Installation: ```bash chmod +x ~/.local/bin/opencode-wrapper echo 'alias opencode="~/.local/bin/opencode-wrapper"' >> ~/.zshrc source ~/.zshrc ``` ## Expected Behavior 1. When terminal is closed (SIGHUP), OpenCode should terminate gracefully 2. All MCP child processes should be killed before OpenCode exits 3. No orphaned processes should remain after terminal closure ## Environment - macOS (arm64) - OpenCode installed via Homebrew - Multiple MCP servers configured (ssh-mcp, figma, arxiv, markitdown, chrome-devtools, github) ## Impact - Memory leak: Each orphaned instance uses 60-90 MB - MCP processes: Additional ~10 MB per MCP per instance - After a week of usage without manual cleanup: 2-3 GB wasted memory
yindo added the perf label 2026-02-16 18:07:34 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 25, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #7261: v1.1.6 still has memory bloat - heap not released + MCP orphan processes (directly mentions orphaned MCP server processes not being terminated when OpenCode exits)
  • #5363: opencode eating 70gb of memory? (related memory accumulation issue)
  • #6119: TUI fails to render with raw ANSI output and critical memory leak on startup (mentions abnormal memory consumption)
  • #9699: Memory usage grows very quickly when running commands with large output (related to memory growth issues)
  • #10046: Uncontrolled Memory Growth Causing System Overload Due to Poor Resource Management (general memory management issues)

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 25, 2026): This issue might be a duplicate of existing issues. Please check: - #7261: v1.1.6 still has memory bloat - heap not released + MCP orphan processes (directly mentions orphaned MCP server processes not being terminated when OpenCode exits) - #5363: opencode eating 70gb of memory? (related memory accumulation issue) - #6119: TUI fails to render with raw ANSI output and critical memory leak on startup (mentions abnormal memory consumption) - #9699: Memory usage grows very quickly when running commands with large output (related to memory growth issues) - #10046: Uncontrolled Memory Growth Causing System Overload Due to Poor Resource Management (general memory management issues) Feel free to ignore if none of these address your specific case.
Author
Owner

@TomasHubelbauer commented on GitHub (Jan 31, 2026):

I am experiencing the same even with no MCP servers: #11527. Looks like a general issue with how OpenCode handles its sub-processes, though I am left wondering why with no plugins nor any MCP servers my OpenCode instance still spawns a sub-process.

@TomasHubelbauer commented on GitHub (Jan 31, 2026): I am experiencing the same even with no MCP servers: #11527. Looks like a general issue with how OpenCode handles its sub-processes, though I am left wondering why with no plugins nor any MCP servers my OpenCode instance still spawns a sub-process.
Author
Owner

@mohamedbouddi7777-dev commented on GitHub (Feb 1, 2026):

Free the world

@mohamedbouddi7777-dev commented on GitHub (Feb 1, 2026): Free the world
Author
Owner

@dollannn commented on GitHub (Feb 8, 2026):

Big issue for me - consuming 28GB of memory

@dollannn commented on GitHub (Feb 8, 2026): Big issue for me - consuming 28GB of memory
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#7555