Skip to content

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

Closed

Description

@s2bomb

Description

The serve and web commands have no graceful shutdown. When the process receives SIGTERM or SIGINT, child resources (MCP subprocesses, LSP servers, file watchers, SQLite connections) are never cleaned up because Instance.disposeAll() is never called.

Separately, the SSE /event endpoint leaks a setInterval heartbeat and a Bus.subscribeAll subscription when the server closes a stream (on InstanceDisposed), because cleanup only runs in onAbort — which Hono only fires on client disconnect, not on stream.close().

Bug 1: Dead server.stop() in serve.ts and web.ts

serve.ts:17-18:

await new Promise(() => {})   // blocks forever — never resolves
await server.stop()           // unreachable

Same pattern in web.ts:78-79. No signal handlers are registered, so SIGTERM/SIGINT use the kernel default (immediate kill, no cleanup).

The correct pattern already exists in worker.ts:137-147:

async shutdown() {
  if (eventStream.abort) eventStream.abort.abort()
  await Promise.race([
    Instance.disposeAll(),
    new Promise((resolve) => { setTimeout(resolve, 5000) }),
  ])
  if (server) server.stop(true)
}

Bug 2: SSE heartbeat interval leaks on server-initiated stream close

server.ts:513-539 — clearInterval(heartbeat) and unsub() are inside stream.onAbort(). Hono's onAbort only fires on client disconnect (via ReadableStream.cancel()). When the server closes the stream on InstanceDisposed via stream.close(), the onAbort callback never fires.

Each server-closed SSE client leaks:

  • One setInterval (10s heartbeat) writing to a closed stream
  • One Bus.subscribeAll subscription processing every bus event

Steps to reproduce

Bug 1:

bun dev serve --port 14096 &
SERVE_PID=$!
sleep 3
kill -TERM $SERVE_PID
wait $SERVE_PID
echo "exit code: $?"

Exit code is 143 (SIGTERM default), zero shutdown logs, child processes survive. Each orphaned process holds ~200-280MB RSS and they accumulate on every restart.

Bug 2:
Connect an SSE client, trigger InstanceDisposed — heartbeat interval keeps firing after stream.close().

OpenCode version

Current dev branch

Operating System

Linux (also affects macOS — any platform running serve or web)

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    perfIndicates a performance issue or need for optimization
    on Feb 18, 2026
  2. github-actions commented on Feb 18, 2026

    @github-actions
    Contributor

    This issue identifies important root causes for some of the memory leak symptoms reported in existing issues. While there are related reports of orphaned processes, this issue provides:

    1. Specific root causes with exact file locations and code examples
    2. Two distinct bugs (graceful shutdown in serve/web + SSE heartbeat leak)
    3. Clear reproduction steps for each issue

    Related issues that may benefit from fixes here:

    This appears to be the most detailed analysis of the shutdown problem in the codebase.

  3. s2bomb commented on Feb 18, 2026

    @s2bomb
    Author

    Hey @thdxr, thanks for making opencode btw, I love it.

  4. s2bomb commented on Feb 18, 2026

    @s2bomb
    Author

    PR with fix: #14092

    This also addresses the orphaned process symptoms reported in #13796 and #12767 for the serve and web code paths.

  5. thdxr commented on Apr 2, 2026

    @thdxr
    Member

    Closing in favor of #20695 where we're collecting all memory-related problems. Please follow that issue for updates.

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

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)perfIndicates a performance issue or need for optimization

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions