TL;DR A command that passes has its whole output saved to a file, so nothing is lost. A command that fails gets a 10,000 character excerpt cut out of the first 30,000, middle removed and no file to fall back on, so on a long suite the failure summary sits at the end and never arrives at all. Put the quiet flag on the command in your
CLAUDE.md, and raiseBASH_MAX_OUTPUT_LENGTHfor the output you cannot trim.
You ask Claude to run the tests. They fail. You tell it to fix them, and it starts editing files that have nothing to do with anything, or it asks you which test broke. It ran the command itself ten seconds ago.
Claude did not misread the failure. It never read it.
How a command's output reaches Claude
Claude Code does not hand the model whatever your command printed. It streams the output to a working file, and once the command exits it reads a 30,000 character window back from that file. What happens next comes down to how the command ended:
| The command | What Claude actually gets |
|---|---|
| Exits clean | Up to around 30,000 characters. Past that, the path to the complete file plus a preview of the start. Nothing is lost: Claude can go read it or search inside it |
| Fails | An excerpt of around 10,000 characters cut from that window, head and tail, with the middle removed. No file, no path |
Read that twice, because it is backwards. When everything works, Claude can recover every last line. When something breaks, which is the one moment the detail matters, it gets a third of the output and the middle is missing.
The proof
A real suite: 481 node --test cases over a shopping cart, each logging two lines the way any real project does. Case 241, the one in the middle, checks VAT rounding and fails. The run prints 74,344 characters and exits 1.
Here is what came back to Claude:
[cart] building fixture 0 with 1 items
[db] SELECT * FROM products WHERE sku IN (...) -- fixture 0
... 96 more lines of fixtures ...
... [20012 characters truncated] ...
[cart] applying discount ladder 61
[db] SELECT
It stops mid-line, on ladder 61. Character 30,000 of the complete file turns out to be applying discount ladder 62.
The arithmetic settles it. 20,012 cut plus 10,000 shown is 30,012: the read-back window is the first 30,000 characters, and the 10,000 character excerpt comes out of that with the middle dropped.
And this is what node prints at the end of those 74,344 characters:
AssertionError [ERR_ASSERTION]: 24.19 == 24.18
at TestContext.<anonymous> (cart.test.js:22:10) {
actual: 24.19,
expected: 24.18,
operator: '=='
}
It never made it into the window. Not the test name, not the file, not the line, not the expected value. Claude got ten thousand characters of SELECT * FROM discounts and the news that something had failed.
Fixing it for good
1. Find your runner's quiet flag
Every one of them has one:
npm test --silent
pytest -q
vitest --reporter=dot
node --test --test-reporter=dot
go test ./... # without -v
cargo test -q
mvn -q test
The same suite on --test-reporter=dot drops from 74,344 characters to 5,787. It arrives whole, AssertionError and all, with nothing cut out.
2. Write it into your CLAUDE.md
This is the step that makes it stick. Leave the command unwritten and Claude types the long version, because that is the one sitting in package.json.
## Project commands
- Tests: `node --test --test-reporter=dot`
- Build: `pnpm build --loglevel error`
- Lint: `eslint . --quiet`
Written once, never thought about again.
3. Widen the window for output you cannot trim
A production log or an inherited build does not always come with a flag. In ~/.claude/settings.json:
{
"env": {
"BASH_MAX_OUTPUT_LENGTH": "150000"
}
}
Run the same suite now and the cut reads ... [63326 characters truncated] ..., with the tail of the excerpt landing on the real end of the output, expected: 24.18 included. It works because runners print the failure summary last, which is exactly what the default window was leaving outside.
One catch: it does not lift the 10,000 character ceiling. All it moves is the tail. You still pay ten thousand characters of filler on every run, and that adds up over a long session. This is the safety net, not the fix.
4. Delegate what you can neither quiet nor widen
A subagent is a disposable context window: send it the 200,000 line log and only the summary crosses back into your conversation. That is habit 9 of the ten for saving tokens, and it earns its place here for the same reason.
Reference
| Value | |
|---|---|
Read-back window (BASH_MAX_OUTPUT_LENGTH) |
30,000 characters, 150,000 maximum |
| Inline ceiling when the command exits clean | ~30,000, then a path to the file |
| Inline ceiling when the command fails | ~10,000, head and tail, no file |
| Output size that gets the command killed | 5 GB |
One detail decides which of those two rows you land in: exit code 1 counts as a clean ending only for grep, rg, find, diff, test, git diff and git grep. Everywhere else, including a jq -e that matched nothing, exit 1 is a failure and the 10,000 character cut applies.
When it is the clock that runs out rather than the output, different variables decide.
Official docs: Tools reference: output limits