Why saved notes fall out of reach
Auto memory is the set of notes Claude writes for itself as it works. You don’t write them. Each project keeps them in ~/.claude/projects/YOUR-PROJECT/memory/: an index called MEMORY.md, plus one topic file per note.
The official memory documentation is specific about what loads: “The first 200 lines of MEMORY.md, or the first 25KB, whichever comes first, are loaded at the start of every conversation.” Topic files are not loaded at startup. Claude reads them on demand, when it decides it needs one.
So a fresh session can reach an older note in three ways. The loaded index names it. The index names a file that names it. Or Claude thinks to search the folder.
The index fills up. Near the limit, Claude Code reminds Claude to “keep one line per entry, move detail into topic files, and merge or drop stale entries”. Over it, the write still succeeds, but Claude gets an error telling it to rewrite the index. Before v2.1.210 there was no error: the extra lines were silently cut on the next load. The message goes to Claude, not your terminal, so you may only see it in the transcript.
Each rewrite is sensible on its own. Over a few weeks, in the folder we traced, older entries moved into dated archive files and sub-indexes, those files were named only from other index files, and some entries left the index entirely. No note was deleted. The path to it just got longer. That folder’s index was 76 lines, well under the limit, and 54% of its notes were still out of reach. Depth, not length, is what buried them.
Check yours, then fix it
You need Python 3.10 or newer. The checker has no dependencies. Read downloaded files before running them, and close any Claude Code sessions on a project before you change its memory.
-
Download the checker. Save memory_reach.py and, if you want to run its 70 tests, test_memory_reach.py in the same folder. Then run:
python3 test_memory_reach.pyCheck: the last line says
OK. Thecheckcommand only reads files; onlyarchive --applywrites. -
Check one project, or every project.
python3 memory_reach.py check ~/.claude/projects/YOUR-PROJECT/memory --show 0 python3 memory_reach.py check --all --show 0--allchecks every folder under~/.claude/projectswith aMEMORY.md. If you moved yours with theautoMemoryDirectorysetting, pass that folder instead. Without--show 0, the report also lists up to ten buried note names. It always prints the folder’s path, so trim that line before you share a report.Check: each folder gets a summary. This one is from the folder we traced, before the fix:
MEMORY.md (as measured): 76 lines, 15,585 bytes; 0 line(s) past the load cut (200 lines / 25,000 bytes) notes: 1207 named in the loaded index: 47 named by a file the index names: 509 listed in the archive tier: 0 NOT within two steps of the loaded index: 651 (54% of notes)Exit code 1 means at least one folder has notes more than two steps away. It is a result, not a crash. Exit code 2 means a folder couldn’t be read, such as a mistyped path or a folder with no
MEMORY.md. No Python? Paste this into Claude Code instead:Check my auto memory for notes the index can’t reach. My memory folder is ~/.claude/projects/YOUR-PROJECT/memory/. Only the first 200 lines (or first 25KB) of MEMORY.md load at session start, not counting YAML frontmatter or block HTML comments, so treat anything past that as invisible. For every other .md file in the folder, decide whether it is within two steps: the visible part of MEMORY.md names it, or names a file that names it. Report the total, how many are within two steps, how many aren’t, and how many lines of MEMORY.md sit past the cut. Don’t change any files.
-
Preview the archive tier. This changes nothing yet:
python3 memory_reach.py archive ~/.claude/projects/YOUR-PROJECT/memoryThe fix is two changes. A new
memory-archive-index.mdlists every note the loaded part ofMEMORY.mddoesn’t name, one line each, with the note’s own description clipped to 140 characters. One pointer line goes near the top ofMEMORY.md. On the traced folder it read:- [Archive tier](memory-archive-index.md): 1160 older notes not listed here, one line each; open it when an older topic, a past decision, or a note name comes up.Check: the preview lists the notes it will add and names only those two files. The pointer sits near the top because a line at the bottom of an over-limit index is cut with everything else. Don’t reorder the rest of
MEMORY.mdto make room; the archive needs one line, not a rebalance. -
Apply it.
python3 memory_reach.py archive ~/.claude/projects/YOUR-PROJECT/memory --applyIt backs up each file before changing it and touches only its own line in
MEMORY.md. IfMEMORY.mdchanges while it runs, it leavesMEMORY.mdalone and asks you to run it again, though it may already have rewritten the archive file; the next run brings that up to date. It has no file locking, which is why sessions should be closed. It won’t replace an archive file it didn’t write unless you pass--force, which backs that file up first, and it never replaces one that is reallyMEMORY.mdor a note under another name. A second run changes nothing, so re-run it whenever Claude rewrites the index.Check: run step 2 again. It reports
NOT within two steps of the loaded index: 0 (0% of notes).
PASS: check reports 0 notes beyond two steps, and the only changes in the folder are the archive file, one pointer line in MEMORY.md, and the backups. If either fails, the card has not passed. Reachable is not the same as found; the next section is what we measured on that.
What we measured, including the misses
On 2026-10-01 we checked the 36 memory folders on one development machine that had a MEMORY.md. Folders with 100 notes or fewer were fine. Eight had more than 100 notes; seven of those had no single archive index, and six of the seven had notes more than two steps out. Three were badly hit, at 76%, 54% and 43%. The worst was 1,142 of 1,511 notes. Across all 36 folders, 2,018 of 4,598 notes were more than two steps away, and 479 of those weren’t reachable from the index by any chain at all.
Then we tested the fix on the traced folder with 16 questions about the project’s past, each with a known answer. A model played the agent. From the loaded index alone, it picked up to three files to open; for each index file it picked, it looked inside only that file and picked notes. Two independent LLM judges, not told which run they were judging, decided whether the opened notes held the answer. A third broke ties. Every “yes” had to quote the note, and each quote was checked mechanically against the file.
| Index | Questions answered (of 16) |
|---|---|
| Before | 5 |
| After, archive built from our own maintenance tool’s output | 14 |
| After, archive built by this script | 14 |
In both “after” runs, the same nine questions flipped to answered and none flipped back (exact McNemar p ≈ 0.004). The agent opened the archive on every question and picked only real files. Before the fix, it had picked 11 filenames that don’t exist.
The two misses were the same in both runs. One question our setup has missed since July. The other’s “correct” answer was wrong: the session had recorded a claim, and the memory later recorded a verified correction, so a memory that learned the correction fails that question.
On 2026-10-03 we re-ran check --all after archiving the two worst folders: 225 of 4,651 notes (5%) are still more than two steps out, all in folders we haven’t touched. That drop is by construction, since the archive lists every note. It shows the fix was applied. The recall table is the evidence that it helps.
What you will see
over its 200-line read limitappears in the transcript when Claude’s write leavesMEMORY.mdlonger than what loads. The documentation’s example of this error ends “Rewrite it to under 140 lines now”. A file over 25KB gets the same error for the byte limit. Each of these rewrites is a chance for older entries to slide out of the index.everything past the limit is silently dropped each time the index is loadedis part of the same message. It means the end of the index is already invisible to new sessions.
Neither message means a note was deleted. The topic files are still on disk; only the route to them got longer.
What this doesn’t show
This is one machine and one person’s projects. Your folders may be fine; small ones were. Counts include dated index files and sub-indexes as notes, and we fixed the two worst folders after measuring them, so today’s machine-wide numbers are lower than the 2026-10-01 ones.
The check counts pointers. It can’t tell whether a description is good enough for Claude to pick the right note. To measure that on your own memory, collect 20 to 30 questions from past sessions with answers quoted word for word, ask using only what loads, judge blind with quotes required, and re-judge one fixed calibration set each round so you notice when the judge moves.
The recall result is single routing runs on one folder. The p-value covers the paired judging, not run-to-run variation, and routing design alone moved the “before” score between 5 and 13. Treat the size of the jump as indicative, not precise.
The load rules come from Claude Code’s documentation, not a probe of its loader. Where the docs are vague, the script errs toward “not loaded”: it reads 25KB as 25,000 bytes and treats a line cut by the byte limit as not loaded. We did not test macOS or Windows paths, subagent memory, or a moved memory folder.