Summary
After closing opencode, multiple LSP server processes remain running in the background indefinitely, consuming significant system memory. This appears to be a systematic issue with process lifecycle management rather than an isolated bug.
Environment
- OpenCode version: Latest (built from source)
- OS: macOS (darwin/arm64)
- Node/Bun: Bun runtime
- Installation:
npm install -g opencode-ai
Current Behavior
When opencode is launched and then closed, LSP server processes spawned for various projects remain alive as orphaned processes:
# After running opencode a few times across different projects
$ ps aux | grep opencode | grep -v grep | wc -l
20
# Memory consumption per process
$ ps aux | grep opencode.*-c | awk '{sum += $6} END {print sum/1024 " MB"}'
~1.2 GB
Root Cause Analysis (from Source Code Review)
1. Missing Process Signal Handlers
In packages/opencode/src/index.ts, only SIGHUP is handled:
process.on("SIGHUP", () => process.exit())
Missing: SIGTERM, SIGINT handlers for graceful shutdown.
2. Forced Exit Without Cleanup Time
packages/opencode/src/index.ts:215:
finally {
// Some subprocesses don't react properly to SIGTERM...
process.exit() // Immediately kills process, no cleanup time for LSP
}
This forces immediate exit without allowing Instance.dispose() or State.dispose() to complete.
3. Instance Cache Never Expires
packages/opencode/src/project/instance.ts:15:
const cache = new Map<string, Promise<Context>>() // Permanent cache, no TTL
Instances (and their LSP clients) are never cleaned up automatically.
4. LSP Processes Not Properly Tracked
packages/opencode/src/lsp/server.ts uses spawn() without process group management:
- No
detached: true with process group tracking
- Parent death doesn't trigger automatic child cleanup
- Each new project creates new LSP servers that outlive the parent
5. State Disposal Timing Issues
packages/opencode/src/project/state.ts:39-46 warns about disposal taking too long (10s timeout), but the forced process.exit() in index.ts prevents disposal from completing at all.
Expected Behavior
- When opencode exits (normally or via signal), all child LSP processes should be terminated
- LSP servers should have idle timeout to auto-cleanup
- Orphaned processes from previous runs should be detected and cleaned up
Steps to Reproduce
- Open a project with opencode:
opencode
- Work for a while (LSP servers will spawn)
- Close opencode (Ctrl+C or normal exit)
- Check for lingering processes:
ps aux | grep opencode.*-c
- Repeat with different projects - processes accumulate
Proposed Solutions
Short-term (User Workaround)
Add to shell configuration:
# Kill orphaned opencode LSP processes on terminal exit
trap 'pkill -f "opencode.*-c"' EXIT
Long-term (Code Fixes)
-
Add signal handlers in index.ts:
process.on("SIGTERM", gracefulShutdown)
process.on("SIGINT", gracefulShutdown)
async function gracefulShutdown() {
await Instance.disposeAll()
process.exit(0)
}
-
Remove forced exit in index.ts finally block, or add delay:
finally {
// Give time for cleanup before forcing exit
await new Promise(r => setTimeout(r, 2000))
process.exit()
}
-
Add LSP idle timeout in lsp/index.ts:
// Auto-shutdown LSP servers after idle period
setTimeout(() => {
if (noRecentActivity) client.shutdown()
}, IDLE_TIMEOUT)
-
Process group management in lsp/server.ts:
spawn(command, args, {
detached: true,
// Track PIDs for cleanup
})
Impact
- Memory: Each LSP process consumes 50-100MB, easily accumulating to 1-2GB+
- System resources: Zombie processes consume file descriptors and CPU cycles
- User experience: System slowdown, need for manual cleanup
Related Code References
packages/opencode/src/index.ts:52, 215
packages/opencode/src/project/instance.ts:15, 103-105
packages/opencode/src/project/state.ts:31-69
packages/opencode/src/lsp/server.ts (all spawn calls)
packages/opencode/src/lsp/client.ts:238-244 (shutdown method exists but not called)
packages/opencode/src/lsp/index.ts:141-144 (dispose handler registered but not triggered)
Severity: High - affects all users with multi-project workflows
Type: Bug / Memory Leak
Summary
After closing opencode, multiple LSP server processes remain running in the background indefinitely, consuming significant system memory. This appears to be a systematic issue with process lifecycle management rather than an isolated bug.
Environment
npm install -g opencode-aiCurrent Behavior
When opencode is launched and then closed, LSP server processes spawned for various projects remain alive as orphaned processes:
Root Cause Analysis (from Source Code Review)
1. Missing Process Signal Handlers
In
packages/opencode/src/index.ts, onlySIGHUPis handled:Missing:
SIGTERM,SIGINThandlers for graceful shutdown.2. Forced Exit Without Cleanup Time
packages/opencode/src/index.ts:215:This forces immediate exit without allowing
Instance.dispose()orState.dispose()to complete.3. Instance Cache Never Expires
packages/opencode/src/project/instance.ts:15:Instances (and their LSP clients) are never cleaned up automatically.
4. LSP Processes Not Properly Tracked
packages/opencode/src/lsp/server.tsusesspawn()without process group management:detached: truewith process group tracking5. State Disposal Timing Issues
packages/opencode/src/project/state.ts:39-46warns about disposal taking too long (10s timeout), but the forcedprocess.exit()in index.ts prevents disposal from completing at all.Expected Behavior
Steps to Reproduce
opencodeps aux | grep opencode.*-cProposed Solutions
Short-term (User Workaround)
Add to shell configuration:
Long-term (Code Fixes)
Add signal handlers in
index.ts:Remove forced exit in index.ts finally block, or add delay:
Add LSP idle timeout in
lsp/index.ts:Process group management in
lsp/server.ts:Impact
Related Code References
packages/opencode/src/index.ts:52, 215packages/opencode/src/project/instance.ts:15, 103-105packages/opencode/src/project/state.ts:31-69packages/opencode/src/lsp/server.ts(all spawn calls)packages/opencode/src/lsp/client.ts:238-244(shutdown method exists but not called)packages/opencode/src/lsp/index.ts:141-144(dispose handler registered but not triggered)Severity: High - affects all users with multi-project workflows
Type: Bug / Memory Leak