-
Notifications
You must be signed in to change notification settings - Fork 11.3k
Description
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() // unreachableSame 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.subscribeAllsubscription 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)