Skip to content

Memory leak in server mode: unbounded EventTarget listeners added to Effect-ts internal emitter (QL) per session #29204

Closed as not planned

Description

@antomicblitz

Environment

  • opencode version: 1.15.10
  • Mode: opencode serve --hostname 0.0.0.0 --port 4096 (headless / server mode)
  • Platform: Linux x86_64 (Fedora 43, kernel 6.19.6-200.fc43)
  • Runtime: Bun compiled binary (~/.opencode/bin/opencode, 144 MB ELF)

Summary

When running opencode serve, a MaxListenersExceededWarning fires within ~30 seconds of each server start (i.e. after enough client sessions connect). The emitter accumulates listeners indefinitely; memory grows unboundedly across sessions and can reach several GB before the process is killed by OOM or the OS.

Warning output (from journald)

MaxListenersExceededWarning: Possible EventTarget memory leak detected.
11 event listeners added to [QL]. MaxListeners is undefined.
Use events.setMaxListeners() to increase limit

 emitter: QL {
   _events: [Object ...],
   _eventsCount: 1,
   _maxListeners: undefined,
   [Symbol(kCapture)]: false,
   emit: [Function: emit],
   ...
 },
     type: "event",
    count: 11,
      at overflowWarning (node:events:185:19)
      at addListener (node:events:158:22)
      at ~effect/Effect/evaluate (/$bunfs/root/chunk-1h0fh6v6.js:25:4492)
      at runLoop (/$bunfs/root/chunk-1h0fh6v6.js:25:2045)
      at evaluate (/$bunfs/root/chunk-1h0fh6v6.js:25:1435)
      at runTasks (/$bunfs/root/chunk-1h0fh6v6.js:11:33531)
      at afterScheduled (/$bunfs/root/chunk-1h0fh6v6.js:11:33418)

Observed memory pattern (from systemd accounting over ~12 h)

Invocation CPU time Memory peak
1 2 m 52 s 1.1 GB
2 2 m 19 s 9.6 GB
3 22 m 23 s 2.1 GB
4 26 s 683 MB
5 53 m 55 s 4.3 GB
Final (before manual stop) 37 m 57 s 1.5 GB

Memory peaks are non-monotone (restart clears heap) but always balloon well above baseline within minutes. The 9.6 GB spike suggests the leak compounds quickly under concurrent or long-lived sessions.

Root cause hypothesis

The stack points to Effect-ts's internal scheduler (runTasks / afterScheduled / evaluate). Each call to ~effect/Effect/evaluate registers a new "event" listener on an internal QL EventEmitter but does not remove it after the fiber/task completes. In server mode, each client session or SSE subscription forks new fibers, so listeners accumulate proportionally to session count.

QL is a minified class name in the compiled bundle (chunk-1h0fh6v6.js); in source it is likely Queue or a similar bounded-queue abstraction from effect/Queue or effect/Runtime.

Steps to reproduce

  1. opencode serve --hostname 0.0.0.0 --port 4096
  2. Open ≥ 10 client sessions (or reconnect repeatedly from opencode attach)
  3. Observe MaxListenersExceededWarning in stderr within ~30 s
  4. Leave server running for several hours — memory grows into the GB range

Impact

  • Server process OOM-killed or must be manually restarted
  • Breaks unattended / always-on headless deployments (the primary use case for opencode serve)

Suggested fix

Find every .on("event", ...)" / .addListener("event", ...)call on the relevant Effect-ts emitter in the server session/subscription path and pair it with a corresponding.off()or.removeListener()` in the cleanup/finalizer. Alternatively, scope the emitter per session rather than sharing a singleton.

Activity

  1. github-actions commented on May 25, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. d9394 commented on Jun 30, 2026

    @d9394

    opencode server listening on http://127.0.0.1:4096
    MaxListenersExceededWarning: Possible EventTarget memory leak detected. 11 event listeners added to [MZ]. MaxListeners is undefined. Use events.setMaxListeners() to increase limit
    emitter: MZ {
    _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 (B:/~BUN/root/chunk-yt94mfwj.js:25:4492)
      at runLoop (B:/~BUN/root/chunk-yt94mfwj.js:25:2045)
      at evaluate (B:/~BUN/root/chunk-yt94mfwj.js:25:1435)
      at runTasks (B:/~BUN/root/chunk-yt94mfwj.js:11:35608)
      at afterScheduled (B:/~BUN/root/chunk-yt94mfwj.js:11:35495)
    

    I get the same problem on my cli-server with v1.17.11

  3. MarthinL commented on Jul 15, 2026

    @MarthinL

    FWIW, I keep getting the same error, but I don't have multiple clients as suggested triggers the issue by the steps to reproduce statement. What I have noticed though, is that there is some kind of automatic refresh happening on the web ui (which I notice because it sometimes messes with the scroll position on update). It could be that the memory leak / connections not cleaned up is triggered by some imperfect event handling that re-initialise connections without cleaning out the old one, likely because it (mistakenly) things the old connection is already lost or never existed or some bad assumption like that. Just going to leave the comment here in case it is useful to someone. It is frustrating to see how many times this had been reported and how long it has existed without getting resolved.

  4. github-actions commented on Sep 14, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions