[BUG] ProcessTransport adds global process.on('exit') listener per query() call, causing MaxListenersExceeded warning

Resolved 💬 1 comment Opened Apr 9, 2026 by Abdallah-khalil Closed Apr 9, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Each call to query() from the Claude Agent SDK creates a new ProcessTransport instance. Each ProcessTransport registers a global process.on("exit", handler) listener (to kill the spawned Claude process if the Node.js host exits).

While each listener IS properly cleaned up when the child process exits (process.off("exit", handler)), if there are 11+ concurrent or overlapping queries before any child process exits, the global listener count exceeds Node's default MaxListeners limit of 10, triggering:

MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
11 exit listeners added to [process]. MaxListeners is 10.
Use emitter.setMaxListeners() to increase limit

This happens in extensions and applications that use the SDK to manage multiple concurrent sessions (e.g., VS Code extensions with multiple chat tabs, or applications spawning parallel agent queries).

The relevant code path in the compiled SDK bundle (ProcessTransport / hF class):

// In constructor/initialize:
process.on("exit", this.processExitHandler)

// Cleanup when child exits:
child.once("exit", () => {
  process.off("exit", this.processExitHandler);
});

What Should Happen?

The SDK should either:

  1. Call process.setMaxListeners() to increase the limit proportionally when adding exit handlers, or
  2. Use a single shared exit handler that tracks all active processes instead of one listener per ProcessTransport instance, or
  3. Document the behavior so consumers know to call process.setMaxListeners() themselves.

Option 2 would be the cleanest fix — a single module-level handler managing a Set<ChildProcess> would eliminate the per-instance listener entirely.

Error Messages/Logs

[Extension Host] (node:51900) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added to [process]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limit

Steps to Reproduce

  1. Use the Claude Agent SDK (@anthropic-ai/claude-agent-sdk) in a VS Code extension or Node.js application
  2. Call query() 11+ times concurrently (e.g., multiple chat sessions, parallel agent queries)
  3. Observe the MaxListenersExceededWarning in the extension host console

This is reproducible with any application that maintains multiple concurrent SDK sessions. Each query() call spawns a process and registers a global process.on("exit") listener.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.92 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

VS Code integrated terminal

Additional Information

SDK version: @anthropic-ai/claude-agent-sdk ^0.2.0
Environment: VS Code Extension Host (Node.js), Windows 11
Claude Code version: Latest

Workaround: Consumers can suppress the warning by calling process.setMaxListeners(20) early in their activation, but this shouldn't be necessary — the SDK should manage its own listener count.

Note: This is NOT a memory leak — the listeners are properly cleaned up when processes exit. It's purely a peak-concurrency issue where the number of active ProcessTransport instances temporarily exceeds the default MaxListeners threshold.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗