Skip to content

fix(lsp-daemon): terminate proxy on stdin close to prevent orphaned processes#5561

Open
PaoloC68 wants to merge 2 commits into
code-yeongyu:devfrom
PaoloC68:fix/lsp-daemon-proxy-stdin-eof
Open

fix(lsp-daemon): terminate proxy on stdin close to prevent orphaned processes#5561
PaoloC68 wants to merge 2 commits into
code-yeongyu:devfrom
PaoloC68:fix/lsp-daemon-proxy-stdin-eof

Conversation

@PaoloC68

@PaoloC68 PaoloC68 commented Jun 24, 2026

Copy link
Copy Markdown

Summary

Fixes #5560 — lsp-daemon MCP proxy processes accumulate as orphans across sessions.

When the parent process (opencode) exits, the proxy's stdin closes but dangling daemon socket handles keep the Node event loop alive, leaving the proxy (and its language server children) running indefinitely. Over multiple sessions this accumulates orphaned processes consuming 1+ GB of RAM.

Changes

packages/lsp-daemon/src/proxy.ts:

  • Listen for stdin close event and force process.exit(0) after a 500ms grace period (handles parent killed via SIGKILL)
  • Catch ERR_STREAM_PREMATURE_CLOSE from the server loop instead of propagating it as unhandled (stdin destroyed mid-request)
  • Force process.exit(0) after the server loop returns normally when running on real stdin

All three paths are guarded by isRealStdin (input === process.stdin) so test harnesses using synthetic streams are unaffected.

packages/lsp-daemon/test/proxy.test.ts:

  • Added regression test: stdin destroyed mid-session → proxy resolves cleanly instead of hanging

Test Results

 Test Files  10 passed (10)
      Tests  48 passed (48)

Root Cause Analysis

The for await (const chunk of input) loop in readStdioJsonRpcMessages exits on EOF, but:

  1. If a daemon request is in-flight when stdin closes, the loop may not exit promptly
  2. Even after the loop exits, open daemon socket connections keep the event loop alive
  3. destroy() on stdin throws ERR_STREAM_PREMATURE_CLOSE which was unhandled

Summary by cubic

Terminate the lsp-daemon MCP proxy when stdin closes to prevent orphaned processes and memory leaks. Also handle premature stream closes and exit after the server loop so dangling sockets can’t keep the process alive.

  • Bug Fixes
    • Listen for stdin close and force-exit after a 500ms grace period (real stdin only).
    • Catch ERR_STREAM_PREMATURE_CLOSE from the server loop instead of propagating it.
    • Exit after the server loop returns when using real stdin.
    • Add regression test: destroying stdin mid-session resolves the proxy.

Written for commit 6d081a8. Summary will update on new commits.

Review in cubic

…rocesses

When the parent process (opencode) exits, the MCP proxy's stdin closes but
the proxy can survive indefinitely due to dangling daemon socket handles
keeping the Node event loop alive. Over multiple sessions this accumulates
orphaned proxy processes (and their associated language servers), consuming
hundreds of MB of RAM.

Two fixes:
- Listen for stdin 'close' and force-exit after a 500ms grace period
- Catch ERR_STREAM_PREMATURE_CLOSE from the server loop (parent killed
  mid-request) instead of propagating it as an unhandled error
- Force process.exit(0) after the server loop returns normally when running
  on real stdin, so dangling handles cannot keep the process alive

Includes regression test: stdin destroyed mid-session → proxy resolves.
@github-actions github-actions Bot added the lsp-daemon Changes under packages/lsp-daemon label Jun 24, 2026
@MoerAI

MoerAI commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks @PaoloC68. Your orphan-prevention fix for the lsp-daemon proxy reviews cleanly and pairs well with the idle-timeout change in #5461. I cannot merge it yet: the test (ubuntu-latest) check is red on dev itself due to a pre-existing failure in packages/shared-skills/provenance-gate.test.ts, unrelated to your change. Once dev is green again I will re-run and merge. Nothing needed from you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lsp-daemon Changes under packages/lsp-daemon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: lsp-daemon MCP proxy processes accumulate as orphans across sessions

2 participants