TL;DR Si el comando pasa, la salida entera se guarda en un fichero y no pierdes nada. Si falla, Claude recibe un extracto de unos 10.000 caracteres cortado de los primeros 30.000, con el centro borrado y sin fichero al que volver, así que en una suite larga el resumen del fallo va al final y no llega nunca. Escribe el comando con su flag silencioso en el
CLAUDE.md, y subeBASH_MAX_OUTPUT_LENGTHpara la salida que no puedas recortar.
Le pides a Claude que pase los tests. Fallan. Le dices que lo arregle y empieza a tocar ficheros que no tienen nada que ver, o peor, te pregunta cuál ha fallado. El comando lo ha ejecutado él hace diez segundos.
No es que no lo entienda. Es que no lo ha leído.
Cómo llega la salida de un comando
Claude Code no le pasa al modelo lo que imprime tu comando tal cual. Lo escribe en un fichero de trabajo mientras corre, y al terminar lee de vuelta una ventana de 30.000 caracteres. Lo que pasa a partir de ahí depende de cómo haya acabado el comando:
| El comando | Lo que le llega a Claude |
|---|---|
| Termina bien | Hasta unos 30.000 caracteres. Si se pasa, la ruta del fichero completo y un preview del principio. No se pierde nada: puede ir a leerlo o a buscar dentro |
| Falla | Un extracto de unos 10.000 caracteres cortado de esa ventana, cabeza y cola, con el centro borrado. Sin fichero y sin ruta |
Léelo dos veces, porque es al revés de como debería ser. Cuando todo funciona, Claude puede recuperar hasta la última línea. Cuando algo se rompe, que es el único momento en el que el detalle importa, recibe la tercera parte y encima le falta el medio.
La prueba
Una suite de verdad: 481 tests de node --test sobre un carrito de la compra, cada uno con sus dos líneas de log, como cualquier proyecto real. El test 241, el del medio, compara el IVA y falla. La salida son 74.344 caracteres y el proceso sale con código 1.
Esto es lo que recibió Claude:
[cart] building fixture 0 with 1 items
[db] SELECT * FROM products WHERE sku IN (...) -- fixture 0
... 96 líneas más de fixtures ...
... [20012 characters truncated] ...
[cart] applying discount ladder 61
[db] SELECT
Se corta a media línea, en el ladder 61. Fui a mirar qué hay en el carácter 30.000 del fichero completo: applying discount ladder 62.
La cuenta cierra sola. 20.012 recortados más 10.000 mostrados son 30.012: la ventana de lectura son los primeros 30.000 caracteres, y de ahí se corta un extracto de 10.000 dejando fuera el centro.
Y esto es lo que node imprime al final de esos 74.344 caracteres:
AssertionError [ERR_ASSERTION]: 24.19 == 24.18
at TestContext.<anonymous> (cart.test.js:22:10) {
actual: 24.19,
expected: 24.18,
operator: '=='
}
Nunca entró en la ventana. Ni el nombre del test, ni el fichero, ni la línea, ni el valor esperado. Claude recibió diez mil caracteres de SELECT * FROM discounts y la noticia de que algo había fallado.
Cómo dejarlo arreglado
1. Busca el flag silencioso de tu runner
Todos lo tienen:
npm test --silent
pytest -q
vitest --reporter=dot
node --test --test-reporter=dot
go test ./... # sin -v
cargo test -q
mvn -q test
La misma suite de antes, con --test-reporter=dot, baja de 74.344 caracteres a 5.787. Entra entera, con su AssertionError incluido, y sin recortar nada.
2. Escríbelo en el CLAUDE.md
Este es el paso que hace que dure. Si el comando no está escrito, Claude teclea la versión larga, porque es la que sale en el package.json.
## Comandos del proyecto
- Tests: `node --test --test-reporter=dot`
- Build: `pnpm build --loglevel error`
- Lint: `eslint . --quiet`
Se escribe una vez y ya no vuelves a pensar en ello.
3. Para lo que no puedas recortar, ensancha la ventana
Un log de producción o un build heredado no siempre tienen flag. En ~/.claude/settings.json:
{
"env": {
"BASH_MAX_OUTPUT_LENGTH": "150000"
}
}
Con la misma suite, el recorte pasa a ... [63326 characters truncated] ... y la cola del extracto ya es el final de verdad de la salida, con su expected: 24.18 dentro. Funciona porque los runners imprimen el resumen del fallo al final, que es justo lo que la ventana por defecto se dejaba fuera.
Con un matiz importante: no sube el techo de los 10.000. Solo mueve la cola. Sigues pagando diez mil caracteres de relleno en cada tirada, y eso se nota en una sesión larga. Es la red de seguridad, no la solución.
4. Y lo que no se pueda callar ni ensanchar, delégalo
Un subagente es una ventana de contexto desechable: le mandas el log de 200.000 líneas y a tu conversación solo vuelve el resumen. Es el hábito 9 de los diez para ahorrar tokens, y aquí sirve por la misma razón.
Referencia
| Valor | |
|---|---|
Ventana de lectura (BASH_MAX_OUTPUT_LENGTH) |
30.000 caracteres, máximo 150.000 |
| Tope inline si el comando termina bien | ~30.000, y a partir de ahí ruta al fichero |
| Tope inline si el comando falla | ~10.000, cabeza y cola, sin fichero |
| Salida máxima antes de que lo maten | 5 GB |
Un detalle que decide en cuál de las dos filas caes: salir con código 1 cuenta como final correcto solo para grep, rg, find, diff, test, git diff y git grep. Para todo lo demás, incluido un jq -e sin resultados, código 1 es un fallo y se aplica el recorte de 10.000.
Si en vez de cortarse la salida lo que se corta es el tiempo, eso se decide con otras variables.
Documentación oficial: Tools reference: output limits