[Bug] Memory leak causing excessive RAM usage since v2.1.118
Status Fixed / completed
Reported on v2.1.121
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 7 comments · opened Apr 28, 2026 · closed May 5, 2026
Bug Description
Since v2.1.118, Claude ram usage is growing. I just started a session 30 seconds ago, didn't do anything and it is already using 10GB or ram. For a reason, it only happens in one project.
How can I diagnose what is wrong with this one and not the others ?
Environment Info
- Platform: linux
- Terminal: kgx
- Version: 2.1.121
- Feedback ID: 41d5079c-e068-4757-b35d-3033cb1126d3
Errors
[]
7 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Same regression on ARM64 / Raspberry Pi 5 (8 GB), Debian 13, kernel 6.12.75. Adding data here since #54572 is a duplicate of this thread.
Environment
claude-opus-4-7[1m]OOM kill signature (5 consecutive kills, dmesg)
Notable: virtual size is consistently ~75 GB while RSS is ~5.9 GB. The 75 GB virtual on a process that only commits 6 GB physical is unusual — suggests very large reserved/mmap'd regions that don't get released, consistent with the ArrayBuffer / V8 heap accumulation pattern from prior regressions (#28763 fixed in 2.1.58, #34317 on RPi 5 in 2.1.71). Looks like the leak path has re-emerged in the 2.1.118+ window.
Workaround for ARM64 users: downgrade pins around 2.1.58 / 2.1.117 are the last known-good points before this regression.
I can add a bare-metal Linux
2.1.123datapoint that may help avoid a recurring confusion in these reports:VmPeak/VmDatashow virtual address-space reservation; the actionable bug is resident/native growth (RssAnon,Private_Dirty,Pss[WKFastMalloc], swap, cgroup peaks) on top of that envelope.Lead hypothesis: on the
2.1.123Bun-SEA / JavaScriptCore build, the long-session resident growth is concentrated in[anon:WKFastMalloc](JSC's bmalloc-derived JS-object pool). It is not mimalloc, not glibc[heap], and not theJSGigacagevirtual reservation that makestop/VmPeaklook alarming. Diagnostic one-liner:I am commenting here rather than opening another issue because the duplicate bot already pointed this thread at #48111 / #51077 / #54142, and the ARM64 datapoint above reports the same broad shape (
~75GB total-vmwith ~6GB RSS). This x86-64 capture adds a more specific allocator attribution plus bare-metal Linux evidence.Environment
2.1.123claude fresh, normal Read/Edit/Bash/subagent work), typically hour-to-workday sessions; three user-visible failures were observed across three consecutive daysapp.slice MemoryHigh=20G,MemoryMax=24Gto prevent desktop freeze; not the root causeLive
/proccapture from host namespaceCaptured from the host (not a sandbox namespace) using
/proc/<pid>/status+/proc/<pid>/smaps_rollupat2026-04-30 ~19:37 BRT:| PID | argv | threads | VmPeak | VmData | VmHWM | VmRSS | RssAnon | VmSwap |
|---:|---|---:|---:|---:|---:|---:|---:|---:|
| 27342 |
.../versions/2.1.123 --chrome-native-host| 4 | 135,515,980 kB | 70,605,332 kB | 217,972 kB | 104,960 kB | 70,300 kB | 0 kB || 172109 |
claude fresh| 30 | 135,515,996 kB | 73,190,080 kB | 962,288 kB | 594,832 kB | 485,072 kB | 237,664 kB || 267843 |
claude --settings {"autoConnectIde":false} fresh| 26 | 135,515,996 kB | 73,513,452 kB | 989,352 kB | 630,120 kB | 517,816 kB | 131,276 kB || 1182856 |
claude| 21 | 135,515,980 kB | 73,044,692 kB | 580,544 kB | 463,088 kB | 351,872 kB | 0 kB |Important caveat: this table alone is not proof of resident RAM leakage. Current RSS is modest while
VmPeak/VmDataare huge, so the first-order signal is deterministic virtual reservation. The resident-growth issue is visible inRssAnon,Private_Dirty,VmHWM, swap, and prior cgroup/systemd peaks.Allocator attribution: WKFastMalloc is the resident surface
A same-host per-VMA pass over
/proc/<pid>/smapsshows the dominant resident allocator is[anon:WKFastMalloc]:| PID | comm | WKFastMalloc virtual | WKFastMalloc Pss | occupancy |
|---:|---|---:|---:|---:|
| 27342 |
2.1.123| 275,812 kB | 61,732 kB | 22.4% || 172109 |
claude| 1,319,652 kB | 393,860 kB | 29.8% || 267843 |
claude| 1,081,588 kB | 273,396 kB | 25.3% || 1182856 |
claude| 738,796 kB | 255,976 kB | 34.6% |For PID 172109,
Pss[WKFastMalloc]is ~394MB against ~519MB total Pss in the latest host capture. That makes JSC's bmalloc-derived JS object/string/state pool the monitoring target, not VIRT and not process-wide RSS alone.What this is NOT the leak
VmPeak ≈ 135.5GBby itself. JavaScriptCore reserves large virtual address regions forJSGigacage/JSStructureHeap; most of it is unbacked. Seeing a huge VIRT column on a Bun/JSC process is expected. The bug report should not lead with that as “RAM used.”[anon:mimalloc]regions appear in the relevant VMAs. Bun may use mimalloc for runtime/native allocations, but JSC JS objects go through bmalloc/WKFastMallocin this build.[heap]as in some prior reports. This shape is closer to retained JSC heap / response-body / async-generator / JS object lifetime than to a glibc heap-only leak.HeapHelperthreads and a larger Bun pool are architectural for active sessions; 24-30 threads is not itself the leak./usage, and does not involve long-running tools with missing progress events; normal Read/Edit/Bash calls return promptly.Why it still belongs in the memory-leak thread
On the same host, separate incidents produced resident pressure and user-visible failure:
2026-04-28, Claude Code2.1.122: one nested long-lived session was OOM-killed aroundanon-rss ~13.5GB.2026-04-30, Claude Code2.1.123: several long-lived sessions accumulated enough resident/cgroup pressure thatapp.slice MemoryHigh=20Gthrottled the desktop path; no kernel-wide OOM, but the session became unusable.VmHWM ~0.55GB-0.94GB,RssAnon ~0.35GB-0.52GB, and some swap, without images or repeated/usage.So the actionable statement is: the huge virtual envelope may be benign; the WKFastMalloc-backed resident growth and prior multi-GB
RssAnon/cgroup peaks are not.Two asks for maintainers
VmPeak ~135.5GB/VmData ~70-73GBenvelope expected for the standalone Linux2.1.123Bun/JSC build? If yes, it would help to document that reporters should not treat VIRT/VmPeak alone as a leak./heapdumpartifacts I am collecting, let me know ifPss[WKFastMalloc]time series,pmap -X, fullsmaps, orstrace -f -e brk,mmap,munmapcaptures would be useful for diagnosis.I have a local capture helper (
polvo-claude-mem-capture) that bundles/procstatus,smaps_rollup,pmap, and recent heapdump files, and can attach sanitized outputs if that helps.x86_64 / 2.1.123: hit this hard, with a new manifestation worth flagging
Joining this thread with x86_64 evidence — every prior datapoint I see is ARM64 (#54572) or unspecified Linux (the original report here). My case adds Ubuntu/x86_64, a much larger magnitude, and a previously-unreported manifestation: the leak was in a forked worker process, not the main
claude.Environment
~/.local/bin/claudesymlink later auto-updated to 2.1.126 mid-session — see "auto-update angle" below)claude-opus-4-7[1m](Opus 4.7, 1M context tier — same as #54572)OOM kill (verbatim from
/var/log/syslog)~19 GB anonymous RSS at kill time. Roughly 3× @ernens's ARM64 magnitude in #54572 (5.9 GB) and 2× @ctruchi's "10 GB after 30 seconds" in this thread.
New manifestation: forked worker, not main process
The killed process was not my main
claude -csession. It was a child worker the main process spawned, identifiable by itsargv[0]set to the version string"2.1.123"(Claude Code seems to use this convention to distinguish forked workers from the mainclaudeinpsoutput).Evidence of the parent/child relationship:
vte-spawn-9fa6aad9-...scopeclaude -c, my session in that terminal) is in the same cgroup/proc/137408/exeis mapped to the 2.1.123 binary — i.e. running the buggy version (see auto-update angle)That distinction matters because the in-tab claude (137408) genuinely could not see what spawned the worker — it was an internal harness/SDK fork, not anything visible to the conversation layer. Self-diagnosis from inside claude returns nothing useful, but cgroup forensics confirm the lineage.
Auto-update angle (likely amplifier)
Timeline (PDT):
| Time | Event |
|---|---|
| 14:54:50 | Started
claude -c(loaded 2.1.123 binary into PID 137408) || 14:57:37 |
~/.local/bin/claudesymlink auto-updated to point at 2.1.126 || 14:57 – 15:36 | PID 137408 keeps running 2.1.123 (Linux retains the mapped binary even after the file is replaced); spawns the worker that leaks |
| 15:36:18 | Worker reaches 19.2 GB RSS, OOM-killed |
So
claude --versionreports 2.1.126 (the new symlink target), but the actual leaking process was running 2.1.123. Auto-update creates a window where existing sessions are "ticking" on the bad binary even after the fix is on disk. After the kill, I checked all my liveclaudeprocesses — 4 of 5 were still running 2.1.123 (the only safe one was started after the OOM, hence picked up the new binary).If maintainers want a clean fix-validation story, please consider that for users who keep claude sessions running across auto-updates, simply shipping a fix is insufficient — there needs to be either (a) auto-restart of in-place sessions on update, or (b) the leak's blast radius bounded enough that an orphaned old binary doesn't take the box down.
What the worker was doing
Asked the parent claude session to mine its own JSONL and tell me what was in flight at the time of the OOM. Findings, paraphrased from its report:
grep(chainedgrep -oE | grep -i | head) over a ~2 MB session-history JSONL. The harness-task-ID wasbu8gdgs2h. This was not requested as a background task — the harness auto-backgrounded it because the chained pipeline was slow. It had been alive for ~13 minutes by OOM time.The strong implication: the leak/runaway lives in harness-side infrastructure, not in any tool call the conversational session issued. Nothing user-driven in the relevant window can plausibly account for a 19 GB working set on its own (largest file processed by any tool: 2 MB; largest tool result: 83 KB). Hypotheses worth maintainer attention, in priority order:
bu8gdgs2his suspicious.Being precise about uncertainty: none of these are proven. What's established is the negative — none of the user-visible tool calls in the window can plausibly account for 19 GB on their own.
Impact
System became unresponsive — keyboard input ignored, display frozen, swap thrashed for several minutes before systemd-oomd's PSI threshold tripped. I had to switch to a tty (Ctrl+Alt+F5/F6), wait for the kernel to act, log back in, then manually kill remaining memory pressure to recover the desktop. The 1m / 5m / 15m load average was 1.49 / 49.95 / 57.69 by the time I got a shell back — the machine spent the better part of an hour either thrashing or recovering.
Took about an hour of my time between the freeze, the recovery, the debug, and this writeup. Flagging that not as complaint-for-its-own-sake but as context: this is not a minor-inconvenience bug. For Linux/x86_64 users running long-lived
claude -csessions on the 1M-context tier, a single forked worker can take the whole machine down. Closing this without a fix would mean the time and forensics here got ignored — please don't.Happy to provide additional data, run an instrumented build, or test fix candidates against my repro setup.
x86_64 / Node.js / 2.1.126 — V8 build datapoint (no OOM, but swap growth + input lag)
Adding a Node.js/V8 build datapoint to complement the Bun/JSC reports above. No OOM kill, but measurable swap growth and input lag during long sessions.
Environment
| | |
|---|---|
| Claude Code | 2.1.126 |
| Build | Node.js v22.22.0 (V8, not Bun/JSC) |
| OS | Fedora 43, kernel 6.19.14-200.fc43.x86\_64 |
| Arch | x86\_64, bare-metal |
| RAM | 62 GB total, ~53 GB free during session |
| Swap |
/dev/zram08 GB lzo-rle (compressed RAM swap, no disk I/O) || swappiness | 40 |
/proc/<pid>/status— single session, ~40 min, moderately active/proc/<pid>/smaps_rollupLargest anonymous mappings (V8 sandbox cage)
No
[anon:WKFastMalloc]regions — consistent with V8 build, not JSC/Bun.Observations
ps, big JSON files, context accumulation) — pages become cold between turns and get evicted to zram.Not observed here
WKFastMallocgrowth (V8 build)Happy to run additional captures (
pmap -X,smaps, heapdump) if useful for the V8 build path.Bare-metal Linux x86_64 / 2.1.126: corroborates @machineghost's forked-worker shape, plus a controlled-window WKFastMalloc time-series
Adding a second x86_64 datapoint that lines up almost exactly with @machineghost's report from the same day, plus a 30-second
Pss[WKFastMalloc]capture across two long-livedclaude freshsessions and the cgroup peak that almost took the box down.Same auto-update window, same artifacts on disk
Mirroring the timeline @machineghost described: my
~/.local/bin/claudesymlink was rewritten to 2.1.126 at2026-04-30 23:18:06 -0300, but~/.local/share/claude/versions/still holds the buggy binaries:In my case, all current sessions started after the symlink flip and
/proc/<pid>/exeresolves to2.1.126. So I am not, today, running an orphan 2.1.123 — but the on-disk hysteresis is real and confirms that an in-placeclaude -cthat was up before 23:18 BRT yesterday would still be running the leak-prone binary. Worth re-stating @machineghost's ask: a one-line client-side warning ("a newer Claude Code is on disk; restart this session to pick it up") would prevent a whole class of recurrence.Pss[WKFastMalloc]across two liveclaude freshsessions, 6 samples × 5sCaptured
2026-05-01 02:59:59Z → 03:00:25ZUTC, idle (no user-initiated tool calls in window). Both PIDs areclaude freshlong-lived sessions on the 1M-context tier:| PID | comm | threads | VmPeak | VmRSS | RssAnon | WKFastMalloc Pss | occupancy in Pss |
|----:|---|----:|----:|----:|----:|----:|----:|
| 1198999 |
claude| 27 | 135,516,408 kB | 556,180 kB | 517,240 kB | 312,888 kB | 59.3% of total Pss (527,667 kB) || 1593371 |
claude| 24 | 135,515,996 kB | 466,088 kB | ~362,000 kB | 252,444 kB | ~54% of total Pss |Both processes show the same
VmPeak ≈ 135.5GBenvelope I described earlier. As expected, that is likely JSGigacage + JSStructureHeap reservation per JSC's address-space layout, not committed RAM. The actionable signal isPss[WKFastMalloc]— and it dominates resident state for both processes.Window deltas (ΔRSS, ΔPss[WKFastMalloc]) over 26 seconds while idle:
Idle windows can shrink as libpas's scavenger returns segments to the kernel — visible here. The actionable shape is growth bursts during specific operations (tool calls, JSONL transcript indexing, auto-backgrounded tasks), not continuous steady-state climb. That matches @machineghost's hypothesis #1 ("harness bookkeeping for auto-backgrounded tool tasks") more cleanly than a generic mimalloc-style fragmentation story.
Diagnostic one-liner reproduced from my prior comment, in case anyone wants to repeat it:
Cgroup-level evidence: app.slice nearly hit the brake
Local safety brake:
app.slice MemoryHigh=20G,MemoryMax=24G, set explicitly to prevent desktop freeze. At capture time:MemoryPeak ≥ MemoryHighconfirms the cgroup throttle has been active in this session — i.e., even with the artificial 20 GB ceiling, the multi-claude footprint pushed past it. Without the brake, the trajectory matches the @machineghost OOM at 19.2 GB anon-RSS within a single forked worker.Cross-runtime corroboration: V8/Node.js shows the same envelope
This envelope shape is also visible cross-runtime — not just on Bun/JSC. @sysC0D's V8/Node.js
2.1.126datapoint posted today (Fedora 43, x86_64, zram swap) reports the sameVmPeak ~135.5 GBenvelope withVmHWM ~163 MB/VmRSS ~104 MBand no[anon:WKFastMalloc]regions — instead, three large anonymous mappings (60 GB + 4 GB + 3 GB) consistent with the V8 pointer-compression cage. This cross-build agreement is useful triage signal:VmPeak.Pss[anon:WKFastMalloc]on the Bun/JSC binary; the large unbacked anon cage + zram-evictable cold pages on the Node/V8 binary.Bun-runtime corroboration for the auto-backgrounded-task hypothesis
For maintainers triaging hypothesis #1: there are at least three open Bun upstream issues with the same shape — long-lived child process + retained stdout/IPC buffers in
WKFastMalloc-backed segments, JS heap stable, RSS unbounded:Bun.spawnpolling: 701 MB RSS / 12 h, JS heap flat. Reporter notes "the leak exists outside the JavaScript heap."Bun.gc()does not recover..read()leaves Bun handle + buffer retained indefinitely.The Register also reported on
2026-04-21that Anthropic upstreamed Claude-Code-driven memory fixes into Bun 1.1.13 (libpas scavenger upgrade, "fixes a class of hangs and crashes in long-running processes"). So the cross-team flow is established. The Bun-side instrumentation that helped find #18265 / #21560 is exactly what would help here: heap snapshots before/after the auto-backgrounded task lifecycle, plusbun:jscheapStats()to confirm the bytes are not in the JS heap.Two concrete asks (additive, not duplicate of mine above)
/usageor a side-channel so users can correlate them with RSS jumps. Right now the harness auto-backgrounds slow pipelines transparently and there's no way for an inside-the-conversationclaudeto see what its own harness is holding open. The 1-second-before-OOM completion timing in @machineghost's report is a strong tell that the harness wrapper outlives the underlying process for longer than expected.realpath /proc/self/exeagainstrealpath ~/.local/bin/claudeand warn if they diverge. Cheap, targets exactly the failure mode @machineghost described, and avoids needing in-place restart of every session.I have the full evidence pack (timeseries CSV,
smaps_rollup, cgroup state,polvo-claude-mem-capturebundle) sanitized and ready to attach if maintainers want raw data. Happy to repeat the capture during a window where I deliberately fire a long-running auto-backgrounded task and correlatePss[WKFastMalloc]with the harness-task lifecycle.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.