My Laptop was running out of memory. A day like any other, when you think about it. But in particular, one culprit was throwing OOM Errors when loading previous sessions: Claude Code, Anthropic's CLI tool, casually consuming 1.6GB of RAM on a ~92 lines session. Idling. In it's own lane. Unbothered.
Anyway, I did what any reasonable person would do: go to GitHub Issues page and yell at clouds. Nah, nerdsniped to debug my own CLI/TUI tool that embeds Claude Code's TUI inside a tmux-like pane (along with Codex and Gemini) and uses heuristics and a bunch of headless xterm.js instances to try to mitigate the annoying Infiniscroll(TM) Bug plaguing the Anthropic's -- otherwise very capable and awesome -- tool, I attached strace to it.
The Setup
sudo strace -f -tt -p <Claude Code's PID> -e trace=read,openat,write,close > claude_fs_shennanigans.txt 2>&1
Environment: WSL2, Linux 6.6.87, Claude Code 2.1.something. At the time of profiling, Claude was doing some git operations - checking out files, comparing commits... Nothing exotic, just trying to merge this one "HOLY SH*T IT WORKS DO NOT CHANGE ANYTHING WE'LL SLOWLY MERGE IT BACK INTO MAIN" branch bit by bit.
Finding #1: The Credentials
Claude Code reads its own .credentials.json file 1.9 times per second.
One point nine times per second. Try saying "BADGER BADGER BADGER BADGER BADGER" repeatedly; the chances are, Claude Code read your credentials already at the first furry friend. Twice.
/home/user/.claude/.credentials.json - 339 opens in 181 seconds
The file is 433 bytes. It does not change. It sits there, on disk, being opened, read, and closed, every 0.5 seconds, in batches of six.
Opened, read, closed. BADGER BADGER BADGER -- that's one batch(er) already.
Perhaps it's checking if the credentials have spontaneously changed? Perhaps it doesn't trust its own memory? Does Claude even dream of electric sheep? We may never know.
Finding #2: The Changelog
Claude Code ships with a changelog. A reasonable thing to have. Me? I just tell Claude to commit a one-liner in Latin. But serious programmers actually do find it important to write and I enjoy reading me a conciselly written Changelog. Well, so does Claude Code, apparently. What's a bit less reasonable is reading it 18 times per second.
Eighteen times per second. That's whole lotta badgers.
~/.claude/cache/changelog.md - 70KB file
Read rate: ~18 reads/sec
Total: 683 reads, 5.5 MB in 38 seconds
It's reading its own changelog. Repeatedly. During normal operation. The changelog does not update while the program is running. This is not a philosophical statement about the nature of change - it's just a static file. Well, at least it's not curl'ing it from their GitHub or anything, it reads it locally, and besides -- SSD reads aren't much of a problem, you should be worried about writes and luckily, Claude does somewhat better job at it (more about it later).
Finding #3: The Logs
Every log operation follows this pattern:
-
openat()- open the log file -
write()- write 322 bytes -
close()- close the log file - Repeat
The debug log alone: 70 open-write-close cycles to write 42KB.
| File | Cycles | Bytes/Cycle |
|-----------------|--------|-------------|
| Debug log | 63 | 610 B |
| Statsig logs | 39 | 1.27 KB |
I'm no Javascript or node.js programmer, my knowledge stops at the for loop and alert() but Claude told me that the fs module has a createWriteStream(). It exists (unless Claude hallucinated it). It's documented and everything, Claude wrote. Looking at the ugly camelCase through my innocent Python eyes I'd reckon it has something to do with writing stuff more efficiently, but I digress.
Finding #4: The Session File
Claude keeps a session file. Mine was 129MB. Claude reads the entire file. Then does it again. And again. And again. "Perhaps it changed," thought the machine, and so it read the file again. And again. and again. As if it tried to shake off a false memory.
Session JSONL: 129MB file, 3 opens in 42 seconds = 9 MB/s I/O
Debug log: 111MB file, 20 opens in 42 seconds = 53 MB/s I/O
Combined estimated I/O from file re-reads: 64 MB/s.
To put this in perspective, that's faster than a spinning hard drive can physically read data. Fortunately, it's cached. Unfortunately, it's still being parsed by V8, which brings us to:
Finding #5: The Memory
I also traced mmap/munmap. V8 was allocating 256KB heap chunks at ~31 calls/second. In under a minute, it allocated 728MB of memory mappings.
This is the JavaScript garbage collector doing what garbage collectors do: collecting garbage. The garbage, in this case, probably being the parsed JSON from 100MB+ files that get re-read and re-parsed continuously. [BADGER INTENSIFIES]
The Full Picture
| Problem | Rate | Impact |
|---|---|---|
| Credentials re-read | 1.87/sec | 339 opens for 433 bytes |
| Changelog re-read | ~18/sec | 5.5 MB read in 38 seconds |
| Debug log re-read | 0.48/sec | 53 MB/s I/O (111MB file) |
| Session re-read | 0.07/sec | 9 MB/s I/O (129MB file) |
| Open-write-close cycles | ~1/sec | 70 syscalls for 42KB |
Recommendations (filed as GitHub issue #19459)
- Cache credentials in memory
- Cache the changelog (it's your own file)
- Keep log files open, use buffered writes
- Don't re-read 100MB files - stream or memory-map them
- Consider that
JSON.parse()on a 129MB string might have consequences
The Profiler Scripts
I wrote some Python scripts to parse strace output:
-
strace_profiler.py- Aggregatesread()calls by content type -
strace_file_io_profiler.py- Calculates throughput fromopenat() -
strace_write_profiler.py- Analyzeswrite()patterns -
strace_fd_correlator.py- Maps file descriptors to file paths
Available on request or in the GitHub issue.
Conclusion
Claude Code is a good tool. I use it daily. It also reads its own changelog 18 times per second and opens its credentials file 339 times in three minutes. These are not mutually exclusive statements.
Philip K. Dick warned us about systems that cannot trust their own memories. Claude Code has apparently taken this warning seriously and now verifies its past approximately 18 times per second.
GitHub Issue: anthropics/claude-code#19459
© 2026. Provided for educational purposes. Reproduction in derivative content without permission is prohibited.``
Top comments (0)