Memory leak: API Response bodies (~181 MB each) accumulate per turn and are never GC'd

Resolved 💬 3 comments Opened Mar 12, 2026 by srijan02420 Closed Apr 17, 2026

Summary

Each API turn leaks ~181 MB — a Response object backed by the Anthropic API streaming body is retained by an unsettled PromiseReaction chain and never released. After 7 turns (~5.5 min) the process holds 1.27 GB of unreleased response bodies.

Environment

  • ccVersion: 2.1.72
  • nodeVersion: v24.3.0 (Bun runtime)
  • platform: darwin

Evidence: Two heap snapshots compared

Snapshot 1 — 6.7s uptime:

  • heapUsed: 325 MB, rss: 310 MB, growthRate: 45.8 MB/s (stream in-flight)
  • 1 x native Response object, 181 MB

Snapshot 2 — 332s uptime (~5.5 min later):

  • heapUsed: 8.1 GB (reported), rss: 585 MB, growthRate: 1.76 MB/s
  • 7 x native Response objects, each exactly 185,951 KB = 1.27 GB total
  • 25,502 weakly-held ArrayBuffers (SSE chunk buffers, GC-pending)

Retainer chain (identical for all 7 leaked instances)

PromiseReaction (hidden)
-> Generator (suspended, never completing)
-> JSLexicalEnvironment

  • self: IncomingMessage
  • stream: IncomingMessage
  • (closure) -> JSLexicalEnvironment { response: Response [181 MB] }

-> IncomingMessage.FetchAPI -> Response [181 MB body]

The async iterator reading the SSE stream leaves a suspended Generator retained by a PromiseReaction. The Generator's closure holds IncomingMessage and Response alive indefinitely. One new 181 MB instance leaks per turn.

Impact

  • RSS grows ~39 MB/turn (physical), virtual allocation grows ~181 MB/turn
  • Long sessions (20+ turns) will exhaust memory on typical developer machines

Steps to reproduce

  1. Start a new Claude Code session
  2. Run /heapdump immediately — note 1 x ~181 MB Response
  3. Have several back-and-forth turns
  4. Run /heapdump again — one new ~181 MB Response per turn retained

Expected behavior

After a streaming response is fully consumed and the turn completes, the Response body and IncomingMessage should be released and become eligible for GC.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗