Skip to content

fix(web): dispose idle MCP server instances to prevent process accumulation#15326

Open
lytedev wants to merge 1 commit intoanomalyco:devfrom
lytedev:fix-web-mcp-process-leak
Open

fix(web): dispose idle MCP server instances to prevent process accumulation#15326
lytedev wants to merge 1 commit intoanomalyco:devfrom
lytedev:fix-web-mcp-process-leak

Conversation

@lytedev
Copy link

@lytedev lytedev commented Feb 27, 2026

Issue for this PR

Closes #14091

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Same root cause as #14092server.stop() is unreachable dead code after await new Promise(() => {}) in web.ts, and no signal handlers exist. I also added idle eviction of cached Instance entries, which #14092 doesn't cover: without it, each project switch accumulates MCP server processes (~200MB each) for the entire lifetime of the web server, even if those projects are never revisited.

Signal handlers (SIGINT/SIGTERM/SIGHUP) follow the existing worker.ts pattern. Idle eviction sweeps every 60s and disposes instances idle >5 minutes. I didn't touch serve.ts or the SSE cleanup — happy to add those if this gets traction, or #14092 can cover them.

How did you verify your code works?

Ran patched and unpatched instances side by side on the same machine. After switching between 8 projects over ~30 minutes:

  • Unpatched: 8 sets of MCP servers accumulated, ~1.65 GB RSS in leaked child processes
  • Patched: 0 leaked processes after idle timeout

bun turbo typecheck (16/16 pass), bun turbo test (1172 pass, 0 fail).

Screenshots / recordings

No UI changes.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Feb 27, 2026
@github-actions
Copy link
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search, I found several related PRs that address similar issues with process management and cleanup:

Potentially Related PRs:

  1. fix: resolve memory leaks and zombie processes from missing cleanup handlers #13186 - "fix: resolve memory leaks and zombie processes from missing cleanup handlers"

    • Directly addresses memory leaks and zombie processes from missing cleanup handlers
  2. fix(cli): handle SIGHUP to prevent orphaned processes on terminal close #12718 - "fix(cli): handle SIGHUP to prevent orphaned processes on terminal close"

    • Implements signal handlers (SIGHUP) for graceful process cleanup
  3. fix(tui): add signal handlers to prevent orphaned processes on terminal close #13848 - "fix(tui): add signal handlers to prevent orphaned processes on terminal close"

    • Similar signal handler approach for TUI mode cleanup
  4. fix(opencode): prevent resource leaks in serve/web shutdown and SSE stream cleanup #14092 - "fix(opencode): prevent resource leaks in serve/web shutdown and SSE stream cleanup"

    • Addresses cleanup in web/serve modes, directly related to the web command
  5. fix: resolve memory leak issues across multiple subsystems #14650 - "fix: resolve memory leak issues across multiple subsystems"

    • Broader memory leak fixes that may overlap with this PR's scope

These PRs appear to be addressing the same class of issues (process accumulation, memory leaks, signal handlers, and graceful shutdown) across different modes of the application. PR #14092 seems most relevant as it specifically targets web/serve shutdown issues.

@lytedev lytedev force-pushed the fix-web-mcp-process-leak branch from 11afb64 to 243d613 Compare February 27, 2026 04:59
@github-actions github-actions bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Feb 27, 2026
@github-actions
Copy link
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@lytedev lytedev marked this pull request as ready for review February 27, 2026 05:11
…lation

In `opencode web` mode, switching projects creates new Instance entries (each
spawning MCP server child processes) that are never cleaned up. Unlike the TUI
which calls shutdown()/disposeAll() on exit, the web command blocks forever
with `await new Promise(() => {})` and never reaches its `server.stop()` call.

This causes unbounded process growth — each project switch leaks ~200MB of MCP
server processes (linear, notion, etc.) that persist for the lifetime of the
web server.

Fix:
- Add signal handlers (SIGINT, SIGTERM, SIGHUP) to the web command that call
  Instance.disposeAll() with a 5s timeout before exiting
- Add idle eviction to Instance: track last access time per directory, sweep
  every 60s, and dispose instances idle for >5 minutes
- Clean up lastAccess bookkeeping in dispose() and disposeAll()
@lytedev lytedev force-pushed the fix-web-mcp-process-leak branch from 243d613 to 42f10f1 Compare February 27, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memory leak: serve and web commands leak child processes on exit; SSE heartbeat interval leaks on server-initiated close

1 participant