Skip to content

fix(opencode): Memory Leak - Stop OpenCode refresh from leaking serve processes#2359

Merged
juliusmarminge merged 2 commits intopingdotgg:mainfrom
joshmeads:bug/opencode-refresh-process-cleanup
Apr 29, 2026
Merged

fix(opencode): Memory Leak - Stop OpenCode refresh from leaking serve processes#2359
juliusmarminge merged 2 commits intopingdotgg:mainfrom
joshmeads:bug/opencode-refresh-process-cleanup

Conversation

@joshmeads
Copy link
Copy Markdown
Contributor

@joshmeads joshmeads commented Apr 27, 2026

What Changed

Fixes cleanup for scoped local OpenCode server processes used during provider refresh/model checks.

The refresh scope now terminates the spawned OpenCode process group when the scoped server is closed, preventing orphaned opencode serve processes from accumulating in the background.

Why

OpenCode refresh/model checks could start a temporary local server, but the wrapper process could exit while leaving the actual long-lived serve child alive. Since refresh runs periodically, this could create a new background OpenCode process every cycle and cause memory usage to balloon.

This keeps provider refresh behavior intact and only tightens cleanup for the local refresh-owned OpenCode process. Active OpenCode thread sessions keep their existing lifecycle.

Warning

I couldn't test on windows and the exit logic is slightly different as process.kill(-pid, signal) is POSIX only. It's correct in theory but should be double checked.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Constraint: Keep active OpenCode thread sessions scoped to their own session lifecycle.
Confidence: high
Scope-risk: narrow
Tested: bun run test src/provider/Layers/OpenCodeProvider.test.ts src/provider/Layers/OpenCodeAdapter.test.ts
Tested: bun fmt
Tested: bun lint
Tested: bun typecheck
Tested: bun run build:desktop

Screenshots

  1. After leaving my laptop overnight
Screenshot 2026-04-26 at 15 37 49
  1. Every 60s a new opencode process is launched and never exits.
Screenshot 2026-04-26 at 16 41 16

Note

Medium Risk
Changes process-spawning/termination behavior for the local opencode serve used during provider refresh, including POSIX process-group killing and Windows-specific fallback; mistakes could leave servers running or kill the wrong process. Scope is narrow and covered by a new refresh cleanup test.

Overview
Fixes a refresh-time memory leak by ensuring locally spawned opencode serve processes are reliably cleaned up when the refresh scope closes.

startOpenCodeServerProcess now spawns the server detached on non-Windows and registers a scope finalizer that SIGTERM/SIGKILLs the entire process group (or uses child.kill on Windows), preventing orphaned background servers from accumulating.

Tests update the OpenCodeRuntime test double to track scope finalization and add an assertion that provider.refresh closes the local server scope.

Reviewed by Cursor Bugbot for commit f446782. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix memory leak by terminating OpenCode server processes on scope finalization

  • Spawns the OpenCode server process with detached: true on non-Windows so the child and its subprocesses form a separate process group.
  • Registers a scope finalizer that sends SIGTERM, waits 1 second, then SIGKILL to the process group (non-Windows) or calls child.kill (Windows), ensuring server processes are cleaned up when provider.refresh completes.
  • Adds a closeCalls counter to the test double and a new test asserting the local server scope is closed after refresh.
  • Behavioral Change: OpenCode server processes are now spawned detached on non-Windows and will be forcibly killed on scope close rather than left running.

Macroscope summarized f446782.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 53fbc363-cf98-401c-b361-99374ae5c8c9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Apr 27, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6cd4849. Configure here.

Comment thread apps/server/src/provider/opencodeRuntime.ts
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 27, 2026

Approvability

Verdict: Approved

Straightforward bug fix adding proper child process cleanup finalizers to prevent serve process leaks during OpenCode refresh. Changes are self-contained with test coverage and use standard process termination patterns.

You can customize Macroscope's approvability policy. Learn more.

OpenCode can leave the long-lived serve child in the spawned process group after the wrapper exits. Provider refresh owns only a scoped inventory probe, so cleanup targets that local process group when the refresh scope closes.

Constraint: Keep active OpenCode thread sessions scoped to their own session lifecycle.
Rejected: Disable provider refresh | would hide model and auth state changes.
Confidence: high
Scope-risk: narrow
Tested: bun run test src/provider/Layers/OpenCodeProvider.test.ts src/provider/Layers/OpenCodeAdapter.test.ts
Tested: bun fmt
Tested: bun lint
Tested: bun typecheck
Tested: bun run build:desktop
@joshmeads joshmeads force-pushed the bug/opencode-refresh-process-cleanup branch from 6cd4849 to 71199bb Compare April 27, 2026 00:40
@joshmeads
Copy link
Copy Markdown
Contributor Author

@juliusmarminge unsure why the smoke failed, CI issue?

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge unsure why the smoke failed, CI issue?

idk if it's related to the ongoing github outages or not... CI passes, just the voucher that doesn't work, don't worry bout it

@joshmeads
Copy link
Copy Markdown
Contributor Author

@juliusmarminge Okay cool. If you need any help replicating the issue I'm happy to demonstrate. Great work on the project so far. Thanks

@juliusmarminge juliusmarminge merged commit 3582288 into pingdotgg:main Apr 29, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants