Repository navigation
Closed as not planned
Description
Describe the bug
When running opencode, after the web interface starts, a MaxListenersExceededWarning is printed in the terminal.
Web interface: http://127.0.0.1:65535/
^[^[MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 event listeners added to [iz]. MaxListeners is undefined. Use events.setMaxListeners() to increase limit
emitter: iz {
_events: [Object ...],
_eventsCount: 1,
_maxListeners: undefined,
[Symbol(kCapture)]: false,
emit: [Function: emit],
setMaxListeners: [Function: setMaxListeners],
getMaxListeners: [Function: getMaxListeners],
addListener: [Function: addListener],
on: [Function: addListener],
prependListener: [Function: prependListener],
once: [Function: once],
prependOnceListener: [Function: prependOnceListener],
removeListener: [Function: removeListener],
off: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
listeners: [Function: listeners],
rawListeners: [Function: rawListeners],
listenerCount: [Function: listenerCount],
eventNames: [Function: eventNames],
},
type: "event",
count: 11,
at overflowWarning (node:events:185:19)
at addListener (node:events:158:22)
at ~effect/Effect/evaluate (/$bunfs/root/chunk-hcr6snjj.js:25:4492)
at runLoop (/$bunfs/root/chunk-hcr6snjj.js:25:2045)
at evaluate (/$bunfs/root/chunk-hcr6snjj.js:25:1435)
at runTasks (/$bunfs/root/chunk-hcr6snjj.js:11:33531)
at afterScheduled (/$bunfs/root/chunk-hcr6snjj.js:11:33418)
This looks like listeners may be getting added repeatedly to the same EventTarget or emitter without cleanup, or the default listener limit is being exceeded during normal startup/use.
Version
- opencode: 1.14.50
Environment
- OS: WSL2 Linux 6.6.87.2-microsoft-standard-WSL2 x86_64
- Node.js: v22.22.1
- Bun: not available in PATH
Steps to reproduce
- Start opencode from a terminal.
- Wait until the web interface URL is printed.
- Observe the warning in terminal output.
Expected behavior
No MaxListenersExceededWarning should be printed during normal opencode startup or use.
Additional context
The warning appeared immediately after the web interface URL was printed. The terminal output included ^[^[ immediately before the warning.
Reacted by Ryan Rake, Daniel Gribkov, romainfd-arrakis, Lei JIANG, Patrick Igiraneza and Forrest Hopkins
Metadata
Metadata
Assignees
Labels
No labels
Activity
github-actions commentedon May 20, 2026
This issue might be a duplicate of existing issues. Please check:
MaxListenersExceededWarningduring normal event fanout/startup (GlobalBus related)If your issue is the same as one of those, please add a reaction or comment there instead so we can track it in one place.
sinnce commentedon Jun 28, 2026
Reproducible on 1.17.11 (and 1.17.8), and this is a real listener leak on client disconnect — not a false/benign "normal fanout" warning (cf. #23798). It is the runtime side of the now-closed #15645 / #17628, which still reproduces.
Minimal reproduction (no plugins)
Result:
MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 event listeners added to [..](event name"event", Effectevaluatestack) — identical to the report above.--pure(zero plugins) rules out any plugin cause.Root cause
The
--print-logsoutput shows 16×global event connectedand 0×global event disconnected. The SSE finalizer ineventResponse()never runs on disconnect:packages/opencode/src/server/routes/instance/httpapi/handlers/global.tsThe stream scope isn't tied to the request's abort signal, so a TCP CLOSE-WAIT (client gone) never finalizes the scope →
GlobalBus.off(...)and the 10s heartbeat cleanup never fire. Each event-stream connection permanently leaks oneGlobalBus"event"listener (+ its heartbeat interval). Browsers reconnect the stream routinely, so the count crosses Node's defaultdefaultMaxListeners(10) in normal use; long sessions grow unbounded (the 7GB / unresponsive behavior in #17628).Note
This is not fixed by bumping
setMaxListeners— that hides a genuine unbounded leak. The fix is to interrupt the SSE stream scope when the request aborts so the existingacquireRelease/Stream.ensuringcleanup actually runs.sinnce commentedon Jun 28, 2026
Cross-ref: this is the same
/eventSSE-disconnect listener leak now addressed by #31922 (Closes #22198) — it bounds the per-subscriber SSE backlog and ends/unsubscribes consumers stuck in CLOSE_WAIT, touching exactlyserver/routes/instance/httpapi/handlers/global.ts+event.ts. The mechanism there matches the repro in my comment above (N×global event connected, 0×global event disconnected). Still reproduces on 1.17.11, so worth tracking/merging under #31922.elegracer commentedon Jun 30, 2026
oh come on, this should have been fixed long ago
lalexisj commentedon Aug 7, 2026
Still reproduces on 1.18.15 (Linux, compiled Bun binary). This is a real per-connection listener leak, not a benign fanout warning — the same GlobalBus
"event"emitter, same~effect/Effect/evaluatestack trace, same_eventsCount: 1.Reproduction
The
--print-logsoutput (or~/.local/share/opencode/log/opencode.log) shows:event connectedevent disconnectedeven though all 12 clients closed their connections after 5s.
MaxListenersExceededWarningfires as soon as 11 connections accumulate, exactly as in @sinnce's repro.Impact data from my setup
Over ~1 month of running
opencode webas a monitoring server for an agent pipeline (manyopencode run --attach http://127.0.0.1:4096sessions), the shared log shows:event connected: 2458event disconnected: 4Each attached session permanently leaks one
GlobalBuslistener (+ heartbeat interval + unbounded queue), so any environment where clients reconnect routinely (browser tabs, attached agents) grows the count without bound and the warning recurs constantly.Confirmation of root cause
Isolated test with plain
node:http+effect@4.0.0-beta.83(the exact versions opencode bundles): theres.on("close")→fiber.interrupt→ stream finalizer chain works correctly and runsGlobalBus.off. So the leak is in opencode's SSE handler wiring — the stream scope is not tied to the request abort signal, exactly as @sinnce documented. TheacquireReleasefinalizer inglobal.ts/event.tsnever runs on client disconnect.Workaround for now: restart the web server periodically to reset listeners.
setMaxListenerswould only hide an unbounded leak.Worth re-opening/merging a fix (e.g. #31922) that binds the SSE stream scope to the request abort signal and bounds the per-subscriber backlog.
disse70 commentedon Aug 20, 2026
Confirming this happens outside the web-interface-startup path too — same warning triggers reliably on a local stdio MCP server call, specifically at
getPrompt.Environment
@opengsd/gsd-core@1.11.0'sgsd-mcp-server(stdio transport, spawned viacommand: ["node", "<path>/gsd-mcp-server.js"])Repro
opencode.json(mcp.<name>.type = "local")./gsd-*slash command backed by an MCPprompts/getcall).Trace
(full trace continues through
~effectinternals — same shape reported above)What I ruled out
initializeJSON-RPC request responds instantly and correctly, no errors.npxor a directnode <path>command.mcp.<name>.enabled: false) removes the warning and the stall completely; re-enabling it (with everything else unchanged) reproduces it every time, consistently at thegetPromptcall site shown in the trace.So this looks like listener accumulation in the client-side stdio transport (
send/requestin the bundled chunk) specifically on the request path used byprompts/get, not something a server implementation can work around. Happy to provideopencode.jsonmcp config or anything else that helps narrow it down.evannotfound commentedon Aug 21, 2026
same issue here
lucaspiress commentedon Aug 26, 2026
Seeing the identical warning on Windows 11, opencode v1.18.23 (npm global install), Node v24.18.0:
Same "11 event listeners" count as the original report, on a different OS/Node version — suggests the count isn't strongly driven by plugin load, since I have 2 plugins active (
oh-my-opencode-slim+@opengsd/gsd-core) totaling ~9 registered hook keys, and I could not reproduce it viaopencode serve+ session creation + SSE/eventsubscription in isolation — it seems to require a fuller interactive session/chat-turn lifecycle to trip. Ruled out plugin duplication as the cause on my end (confirmed viaopencode debug config→plugin_origins, no duplicate registrations). Happy to provide more diagnostics if useful.quentingosset commentedon Sep 20, 2026
I can reproduce the stdio MCP /
getPromptvariant described above.Environment:
search-console-mcp2.1.3 (https://github.com/saurabhsharma2u/search-console-mcp/)With this local MCP enabled, starting a new OpenCode session from Orca produces:
The trace goes through the MCP transport
send/requestpath and thengetPrompt.If I set
"enabled": falsefor the MCP in the global OpenCode configuration and restart OpenCode, the warning disappears. Re-enabling the MCP brings the warning back. The MCP tools themselves are usable once the session starts.This looks consistent with the client-side stdio transport issue already described in this issue. The MCP appears to trigger the
prompts/getpath, but the listener accumulation seems to happen in OpenCode's transport rather than in the MCP server.nicholaicascio commentedon Sep 26, 2026
On windows:
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊