●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 32 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●API — In API version 2026-10-21, passing temperature, top_p or top_k returns a 400 error●NEW — I Put a Ten-Line Promise File at the To…●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 32 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●API — In API version 2026-10-21, passing temperature, top_p or top_k returns a 400 error●NEW — I Put a Ten-Line Promise File at the To…
When Only the Back Half of a Subfolder CLAUDE.md Is Ignored, Count Characters, Not Bytes
Since Claude Code 2.1.309, a subfolder CLAUDE.md and path-scoped rules have a character limit, and anything past it is not handed over. This note covers a counting trap I hit on my own files, a scan script, a pre-commit guard, and how to split a file into rules.
I wanted to know whether the CLAUDE.md where I keep my operating rules was being read to the end. The trigger was one line in the Claude Code 2.1.309 changelog: a subfolder CLAUDE.md over the size limit now loads only its start.
I ran ls -l first. It said 16,048. That is well under the 40,000 limit, but not by an enormous margin, and the number alone could not tell me whether I had room to spare.
Here is the conclusion up front. The limit is in characters, not bytes, and for files that are mostly Japanese the number ls gives you cannot be used as it is. Counting again gave 9,000 characters, which left plenty of room.
The limit applies to subfolder files and path-scoped rules
The memory page in the official docs has a troubleshooting section for "Claude ignores the end of my subdirectory CLAUDE.md". It makes three points.
First, which files are covered. A CLAUDE.md or CLAUDE.local.md in a subdirectory of where you started Claude Code, files they pull in with @path, rules in a subdirectory's .claude/rules/, and rules with paths: frontmatter. These are the files that load on demand.
Second, the numbers. With a 200K context window the limit is 40,000 characters. With a 1M window it is 200,000 characters on Opus 4.6 and Sonnet 4.6, and 150,000 on later models.
Third, what happens past the limit. Claude receives only the start of the file, followed by a note telling it to read the rest with the Read tool. Unless Claude chooses to go and read it, the later instructions are not in its context.
File
When it loads
This limit
CLAUDE.md at the launch directory
Always, at session start
Does not apply (a warning past 200 lines)
CLAUDE.md in a subfolder
When you work with files there
Applies
Rules with paths:
When a matching file is in play
Applies
Rules without paths: (directly under .claude/rules/)
Always, at session start
Does not apply
What surprised me is that the file most likely to be the longest, the top-level CLAUDE.md, is not covered by this limit at all. What gets cut is the subfolder file you added later. The docs also note that before 2.1.309 this limit was not applied to on-demand files.
Bytes and characters differ by 1.5 to 1.8 times in Japanese
ls -l and wc -c return bytes. In UTF-8, one Japanese character takes three bytes. Here is what I measured on my own files.
Command
CLAUDE.md
CLAUDE_REFERENCE.md
wc -c (bytes)
16,048
21,387
wc -m (UTF-8 locale)
9,000
14,183
LC_ALL=C wc -m
16,048
21,387
The third row is the point of this note. Under the C locale, wc -m returns bytes, not characters. The official procedure says to count with wc -m, but cron jobs and scheduled runs sometimes have LANG set to C. Counting there makes a Japanese file look 1.5 to 1.8 times larger than it is.
The mistake that hurts runs in that direction: you believe a file is over the limit and trim one that still had room.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You can scan a project's CLAUDE.md and rules files with one command and find the ones whose back half may not be loaded, before you spend an afternoon hunting for why an instruction is ignored
✦You can avoid the trap where wc -m silently returns bytes under some locales, and compare Japanese or other multibyte files with the limit using real character counts
✦You can put a guard in front of commits that stops a file from growing past the limit, and decide how to split it using a procedure you can apply to your own setup
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
# Pin the locale to UTF-8 and count characters (correct for Japanese too)LC_ALL=C.UTF-8 wc -m path/to/CLAUDE.md# Some systems have no C.UTF-8. Try ja_JP.UTF-8 or en_US.UTF-8 insteadlocale -a | grep -i utf
If the number equals what wc -c gives, the locale is still C and you are looking at bytes. When you cannot be sure, let Python do the counting.
Step 2: Scan the whole project
Counting by hand stops being practical as files multiply. This script finds CLAUDE.md and rules files and lists them. Python's len() counts characters, so the locale does not matter.
#!/usr/bin/env python3"""List the character counts of CLAUDE.md and .claude/rules/*.md against the on-demand limit.Usage: python3 instr_size.py <directory where you start Claude Code> [--limit 40000]Exit code 1 if anything is OVER."""import os, sysSKIP = {"node_modules", ".git", ".next", "dist", "build"}def has_paths_frontmatter(text): if not text.startswith("---"): return False head = text.split("---", 2)[1] if text.count("---") >= 2 else "" return any(line.strip().startswith("paths:") for line in head.splitlines())def classify(rel, text): parts = rel.split(os.sep) name = parts[-1] if name in ("CLAUDE.md", "CLAUDE.local.md"): return "always" if len(parts) == 1 else "on-demand" if ".claude" in parts and "rules" in parts: nested = parts.index(".claude") > 0 return "on-demand" if (nested or has_paths_frontmatter(text)) else "always" return Nonedef main(root, limit): rows, over = [], False for dirpath, dirnames, filenames in os.walk(root): dirnames[:] = [d for d in dirnames if d not in SKIP] for fn in filenames: p = os.path.join(dirpath, fn) rel = os.path.relpath(p, root) if not (fn in ("CLAUDE.md", "CLAUDE.local.md") or (fn.endswith(".md") and f"{os.sep}rules{os.sep}" in p and ".claude" in rel.split(os.sep))): continue with open(p, encoding="utf-8", errors="replace") as f: text = f.read() kind = classify(rel, text) if kind is None: continue chars = len(text) flag = "OK" if kind == "on-demand": if chars > limit: flag, over = "OVER", True elif chars > limit * 0.8: flag = "NEAR" rows.append((chars, len(text.encode("utf-8")), text.count("\n") + 1, flag, kind, rel)) for chars, nbytes, lines, flag, kind, rel in sorted(rows, reverse=True): print(f"{chars:>8} chars {nbytes:>8} B {lines:>5} lines {flag:<5} {kind:<9} {rel}") return 1 if over else 0if __name__ == "__main__": limit = 40_000 args = sys.argv[1:] if "--limit" in args: i = args.index("--limit"); limit = int(args[i + 1]); del args[i:i + 2] sys.exit(main(args[0] if args else ".", limit))
I checked it on a throwaway test directory. A 20,000-character Japanese subfolder CLAUDE.md (about 59KB) came back OK, and a 41,000-character English subfolder CLAUDE.md (about 41KB) came back OVER. The Japanese file is 1.4 times larger in bytes, yet the verdict goes the other way.
41000 chars 41000 B 774 lines OVER on-demand app/CLAUDE.md 34000 chars 100342 B 830 lines OK always .claude/rules/api.md 20000 chars 59026 B 488 lines OK on-demand app/sub/CLAUDE.md 12230 chars 36032 B 302 lines OK on-demand .claude/rules/scoped.md 9000 chars 26562 B 220 lines OK always CLAUDE.md
Change the limit with --limit. If you work in a 1M context window, pass --limit 150000. In the folder where I keep my operating rules, the top-level CLAUDE.md is 9,000 characters and counts as "always"; nothing was over the limit.
Step 3: Stop a file before it grows past the limit
Even after a cleanup, rules grow by accretion. Stopping it at commit time works better than remembering to count. The scan script's exit code does the job.
#!/bin/sh# .git/hooks/pre-commit (don't forget chmod +x)# Block the commit if any on-demand instruction file is over 40,000 characterspython3 tools/instr_size.py . --limit 40000 || { echo "pre-commit: a CLAUDE.md is over the limit. Split it into rules or trim it." >&2 exit 1}
In a throwaway repository, committing a 41,000-character file printed the scan list and the commit stopped with exit code 1. After I shortened the file, the commit went through.
Before you cut, think about splitting
When a file goes over, you can trim it or split it. The docs say each rules file counts against the limit on its own. Splitting one file into three rules allows roughly three times the total.
When you split, add paths: so the rule loads only while matching files are in play.
---paths: - "src/api/**"---# Rules for the API layer- Never swallow an error; return it to the caller.- Always put a timeout on outbound calls.
With this in place, work that never touches the API does not spend context on this rule. A swollen subfolder CLAUDE.md can be split the same way.
Pitfalls
Importing with @path does not reduce context. Imported files load together with the file that imports them, and the docs say imports help organization but not context cost. Files imported by a subfolder CLAUDE.md are subject to the limit too.
The 200-line guideline and the 40,000-character limit are different yardsticks. My CLAUDE.md is 104 lines, but its longest line is 485 characters. Watching only line counts hides the weight of long sentences piling up. Version 2.1.311 also fixed long freezes at startup and after edits when a CLAUDE.md contains a very long list item.
The limit depends on the context window you use. After switching, check again whether anything shows NEAR, which means above 80% of the limit.
Cowork sessions read these files the same way. The docs add that in Cowork, an @path in your user-level ~/.claude/CLAUDE.md that points outside the working directory is skipped. That is a separate matter from the limit, but it can also produce "I wrote it and it has no effect".
I did not reproduce the truncation itself. What I ran and confirmed in this note is the counting, the scan script and the pre-commit guard. The truncation behavior rests on the official docs and the changelog.
One thing to do next
Point instr_size.py at your launch directory and run it once. If nothing shows OVER or NEAR, the reason an instruction is ignored lies elsewhere, and you can work through the troubleshooting steps for a CLAUDE.md that isn't followed from the top.
Count characters, and guard the boundary file by file. I intend to hold to that line whenever I add another rule.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.