[Bug] File loading adds 70% token overhead due to line number formatting
Bug Description
Claude Code Context Overhead Issue Report
Date: 2026-01-22
Claude Code Version: VSCode Extension + CLI
Model: claude-sonnet-4-5-20250929
Summary
Claude Code's file loading mechanism (both @ syntax and Read tool) adds approximately 70% token overhead due to line number formatting. Combined with an undocumented 2000-line truncation limit, this significantly impacts context efficiency and causes unexpected behavior.
Problem Statement
Issue 1: Excessive Token Overhead (70% waste)
When files are loaded into context via @filename syntax or the Read tool, Claude Code adds line numbers and formatting that consumes 1.7x the raw token count.
Impact:
- A 31,000 token documentation set consumes 52,700 tokens (21,700 tokens wasted on formatting)
- Users hit context limits much faster than expected
- Context cost is significantly higher than necessary
Issue 2: Undocumented 2000-Line Truncation
Files loaded via @ syntax are silently truncated at 2000 lines without clear warning to the user.
Impact:
- Users believe they've loaded complete files when they haven't
- Critical information beyond line 2000 is silently omitted
- No error message or warning is displayed
- This matches the Read tool's default limit but is not documented for
@syntax
Reproduction & Evidence
Testing Environment:
- ✅ Claude Code CLI (Terminal)
- ✅ Claude Code VSCode Extension
- Both environments exhibited identical behavior - this is not VSCode-specific
Test Setup
We created controlled test files with known token counts and measured the actual context consumption.
Test files created:
# File with 1000 lines of numbers (1\n2\n3\n...)
test_1000_numbers.txt: 2,001 tokens (raw)
# File with 5000 lines of code (const value1 = 1;)
test_5000_short.js: 43,008 tokens (raw)
Test 1: Medium File (1000 lines)
Command: @ claude-token-test/test_1000_numbers.txt
Results:
- Raw tokens: 2,001
- Messages category: 6,400 tokens
- Multiplier: 3.2x ⚠️
Note: This 3.2x includes both formatting overhead AND conversation overhead (Claude's response). The pure file overhead is ~1.7x.
Test 2: Large File (5000 lines)
Command: @ claude-token-test/test_5000_short.js
Results:
- Full file: 43,008 tokens (5000 lines)
- Only first 2000 lines loaded: 16,002 tokens (raw)
- Messages category: 27,300 tokens
- Multiplier: 1.7x ✓
- 63% of file was silently truncated ⚠️
Claude's response confirmed: "I'm seeing the first 2000 lines"
Test 3: Real-World Documentation
6 documentation files loaded via @ syntax:
| File | Lines | Raw Tokens |
|------|-------|------------|
| docs/01-overview/01-project-overview.md | 191 | 1,871 |
| docs/00-current-state/project-status.md | 578 | 5,142 |
| docs/00-current-state/developer-onboarding.md | 718 | 4,683 |
| docs/00-current-state/technical-inventory.md | 572 | 4,350 |
| tasks/conversation-layer--overview.md | 638 | 4,501 |
| tasks/conversation-layer-1--cli.md | 1232 | 10,543 |
| Total | 3,929 | 31,090 |
Results:
- Expected context usage (raw): 31,090 tokens
- Actual Messages category: 54,400 tokens
- Overhead: 23,310 tokens (75% waste)
- Multiplier: 1.75x
Test 4: Read Tool vs @ Syntax Comparison
Both mechanisms exhibit similar overhead:
| Method | File | Raw Tokens | Context Tokens | Multiplier |
|--------|------|------------|----------------|------------|
| Read tool | test_1000_numbers.txt | 2,001 | ~6,300 | 3.15x |
| @ syntax | test_1000_numbers.txt | 2,001 | ~6,400 | 3.2x |
| @ syntax | test_5000_short.js (truncated) | 16,002 | 27,300 | 1.7x |
Note: The 3.x multipliers include conversation overhead. Pure file formatting overhead is consistently ~1.7x.
Root Cause Analysis
Line Number Formatting
The Read tool output includes line numbers in this format:
1→1
2→2
3→3
...
1000→1000
Each line contains:
- Padding spaces (6 characters for alignment)
- Line number (1-4+ digits)
- Arrow separator
→ - Original content
- Newline character
This formatting adds approximately 70% overhead to the raw file content.
Evidence from GitHub Issues
A reported bug documented similar findings:
"A file with 20,010 lines containing just line numbers consumed 59,038 tokens"
This represents a 2.95x multiplier, consistent with our findings.
Impact on Users
1. Context Exhaustion
Users hit the 200k token limit much faster than expected:
- 6 documentation files (31k raw) consume 52k tokens with overhead
- Reduces effective context window by ~40%
- Auto-compact triggers earlier and more frequently
2. Unexpected Costs
For API usage:
- Input tokens are 1.7…
Note: Content was truncated.
Showing cached comments. Read the full discussion on GitHub ↗
12 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
This is NOT a duplicate - it provides comprehensive analysis that unifies multiple scattered reports into a single root cause with quantifiable data and solutions.
How This Report Differs
vs #18218 (Read tool token consumption):
@syntax AND Read tool (not just Read)@raw:syntax, lazy injection)vs #6910 (2000-line limit):
@syntax (undocumented)New Contributions
This Explains Multiple Related Reports
Your report unifies scattered complaints that describe symptoms without identifying the root cause:
Why This Matters
Current state (my real-world testing):
With proposed solutions:
Actionable Solutions Proposed
@raw:filenamesyntax (no line numbers for read-only context)--no-line-numbersoptionThis report provides the data, analysis, and roadmap the team needs to resolve multiple user complaints with a single architectural improvement.
@bcherny - could you please have a look at this issue? This affects all Claude Code users and wastes 40% of context.
Would appreciate urgent review given the widespread impact.
Whoa. This is outstanding. I'm going to try to repro. If this is fact true - we're all getting MASSIVELY hit w/ respect to our 'Max' usage.
@puya any chance you know when this was introduced (how far we should roll back to)? Thank you.
@willshen no unfortunately I'm not sure. I'm not even certain that this wasn't always there!
@puya since line number formatted is roughly a fixed size for each line, wouldn't the overhead % depend on the length of the line and become negligible as the line length increases?
Curious also to learn how you estimated the raw token count for the input files, and see what might be the cause of the additional input token count (maybe it's something other than just the line formatting)
Hey @willshen .. please see explanations below:
Question 1: Shouldn't overhead decrease with longer lines?
Short answer: Yes, you're absolutely correct! And our data confirms this.
The Data Shows Exactly This Pattern
| File Type | Avg Line Length | Raw Tokens | Formatted Tokens | Multiplier | Overhead % |
|-----------|----------------|------------|------------------|------------|------------|
| Numbers only | ~1 char/line | 2,001 | 6,005 | 3.00x | 200% |
| Short code | ~7 chars/line | 7,002 | 11,006 | 1.57x | 57% |
| Longer code | ~23 chars/line | 43,002 | 63,006 | 1.47x | 47% |
Why This Happens
Line number formatting adds a fixed cost per line:
Breaking down the overhead:
→For very short lines (like
1\n):For typical code lines (like
const value = 123;):For very long lines (like documentation paragraphs):
Real-World Impact
You're right that overhead becomes proportionally smaller with longer lines, BUT:
Example: Real Code vs Numbers
Numbers file (
test_1000_numbers.txt):JavaScript file (
test_1000_short.js):Documentation (typical markdown):
So Yes, You're Correct!
The overhead IS line-length dependent. However:
---
Question 2: How did you measure raw tokens?
Method: We Used
tiktoken(OpenAI's Official Tokenizer)Tool used:
token-counterCLI tool (this is my opensource repo [https://pypi.org/project/tokens-cli/](url) that you can also install through "pip install token-counter-cli"Encoding:
cl100k_base(the same encoding Claude uses)Why this is accurate: Claude Code uses the same tokenizer under the hood
Exact Commands Run
Output:
Verification: You Can Test Yourself
Test files linked below with known token counts:
test_1000_numbers.txt
test_1000_short.js
test_5000_numbers.txt
test_5000_short.js
Or use Python directly:
What Else Could Cause Additional Tokens?
You're right to ask! Here's what we investigated:
Hypothesis 1: Conversation Overhead
What it is: Your prompt + Claude's response
Measured: ~200-500 tokens per exchange
Accounted for: Yes, we subtracted this in our calculations
Example:
We subtracted this to isolate just the file overhead.
Hypothesis 2: System Prompt & Tools
What it is: Tool definitions, system instructions
Measured: ~20-40k tokens (shown in
/contextas "System tools", "System prompt")Accounted for: Yes, these appear in separate categories, NOT in "Messages"
From
/contextoutput:Hypothesis 3: Line Number Formatting
What it is: The
1→contentformattingMeasured: By simulating the formatting and counting tokens
Result: This FULLY explains the overhead!
Our simulation:
Hypothesis 4: Hidden Metadata
What it is: File paths, timestamps, etc.
Measured: Tested with simulation - not significant
Result: Minimal impact (<1% of overhead)
Hypothesis 5: VSCode Extension Context
What it is:
<ide_opened_file>tags when you open filesMeasured: Tested in both VSCode AND terminal CLI
Result: Same overhead in both - not VSCode-specific!
Test results:
The Smoking Gun: Simulation Matched Reality
We simulated ONLY the line number formatting and got results very close to actual:
| File | Raw | Simulated | Actual (Claude Code) | Difference |
|------|-----|-----------|----------------------|------------|
| test_1000_numbers.txt | 2,001 | 6,005 | ~6,200 | 195 tokens (3%) |
| test_5000_short.js | 16,002 | 24,002 | ~27,100 | 3,098 tokens (13%) |
Conclusion: Line number formatting accounts for 87-97% of the overhead. The remaining 3-13% is likely:
Could It Be Something Else?
We considered:
The evidence is strong: It's the line number formatting.
How You Can Verify
``
bash
``pip install tiktoken
python -c "import tiktoken; print(len(tiktoken.get_encoding('cl100k_base').encode(open('yourfile.txt').read())))"
@ yourfile.txt/context``
bash
``cd claude-token-test/
python reproduce_overhead_issue.py
---
Summary
Q1: Doesn't overhead decrease with longer lines?
✅ Yes! Our data confirms this (47% for long lines vs 200% for short lines)
❌ But it doesn't become negligible - still 30-70% for most real files
Q2: How did you measure raw tokens & what else could it be?
✅ Used
tiktoken(cl100k_base) - same as Claude uses✅ Investigated 5+ alternative hypotheses
✅ Simulation of line formatting alone explains 87-97% of overhead
✅ All data is reproducible with included test files
The evidence strongly points to line number formatting as the primary cause of overhead, with line-length dependency exactly as you predicted!
---
Files for verification:
claude-token-test/test_*.txt- Test files with known token countsreproduce_overhead_issue.py- Simulation scriptINVESTIGATION_REPORT.md- Full methodologyINVESTIGATION_REPORT.md
reproduce_overhead_issue.py
Try it yourself:
While waiting for an official fix, here are some user-side workarounds that help mitigate the overhead:
1. Delegate file exploration to subagents
Instead of reading files directly in your main session, use
Taskagents to explore codebases. The overhead stays in the cheaper subagent's context, not your main session:The subagent can read dozens of files, but only returns a concise summary to your main session.
2. Use targeted reads with offset/limit
The Read tool accepts
offsetandlimitparameters. Instead of loading entire files:This is especially useful when you know the relevant section (from error messages, grep results, etc.).
3. Route large-context tasks to extended-context models
If you have MCP model routing configured, route file-heavy analysis to models with larger context windows. This preserves your main session's context for interactive work.
4. Grep before Read
Use
Grepto find relevant line numbers first, then do targeted reads:This shows the context around matches without loading entire files.
---
These don't fix the root cause but can significantly reduce the impact until an official fix lands.
Still no updating this !?
I noticed the line number and formatting issue today too and before writing a new issue I will append my points here to prevent this from going stale:
The read tool adds approximately 20% formatting overhead in my test with an XML file. The fixed per line overhead applies to all multi-line content, regardless of context. Line numbers and arrow markers may be useful for agent accuracy in edit contexts, but they add unnecessary overhead for information retrieval reads. Leading whitespaces provide no benefit to the model and represent pure formatting waste (similar to JSON pretty vs compact).
Current Behavior
Test Case Metrics (51KB XML, 1523 lines)
Impact
Expected Behavior
With the update 2.1.86 there was a change in this logic. The line numbers are still there but the numbers are more compact:
The system reminder about malware awareness is unchanged. This is an improvement but overhead is still there.