Make your toolchain's errors legible to the agent
Treat build, test and CI output as an interface the agent reads: put the informative error before the noisy generic one, and give the agent a way to fetch CI logs itself.
The official documentation describes the mechanism this relies on. The technique itself is community practice.
What this costs you if it is wrong
This is real work on your build system with no payoff for human readers, and it is easy to overshoot into rewriting error handling — start with the two or three failures your agent actually gets stuck on. The CI half carries real risk: allowlisting `gh` broadly grants merge, release and secret-setting alongside log reading, and CI logs are untrusted input that can carry both injected instructions and accidentally-printed tokens into your context.
You ask Claude to fix a failing build. It comes back with a confident fix — for the wrong thing. Reading its reasoning, you realise it never saw the actual compiler error. What it saw was 180 lines of stack frames from your test wrapper, then a generic Command failed with exit code 1, and it worked backwards from those.
This is not a prompting problem. It is an output-plumbing problem, and Claude Code's own limits make it sharper than it is for a human at a terminal.
Why a failing command is the worst case
A human skims a wall of output and their eye lands on the one line that matters. Claude gets a fixed-size window, and a failure gets a much smaller one than a success:
| Result | What Claude receives |
|---|---|
Valid (exit 0, or exit 1 from grep/find/diff/git diff and a few others) | Roughly 30,000 characters inline; past that, a saved file path plus a preview it can read or grep for the rest |
| Failure (every other non-zero exit) | Roughly 10,000 characters inline; past that, a head-and-tail excerpt of that size — and no file path |
Three consequences worth sitting with:
- The failure budget is a third of the success budget, and it applies exactly when the output matters most.
- Head-and-tail means the middle is what gets dropped. If your runner prints a banner and a wrapper trace first, and a summary line last, the informative error is in the part that vanishes.
- There is no file path on the failure branch. Claude cannot go back and read the rest. What the excerpt contains is all it will ever have.
What to actually change
Do not rewrite your error handling. Pick the two or three failures your agent visibly gets stuck on and treat them as the interface they are.
Put the informative line before the generic one. If your test runner prints per-case failures and then a summary, make sure the per-case output is not buried under a framework banner. If a wrapper script catches an error and re-raises it, print the original message first and the wrapper's trace after. The rule of thumb: the first ~40 lines of a failure should tell you the file, the line, and what was wrong.
Make the noisy layer quieter, not the useful layer louder. --no-banner, --quiet, --reporter=dot, suppressing progress bars — anything that keeps ANSI churn and per-file ticks out of the excerpt. Claude Code already strips ANSI escape sequences from captured error output, but the text those sequences decorated still costs you budget.
Filter at the hook layer when you cannot change the tool. A PreToolUse hook on Bash can rewrite a test invocation to a failures-only form, or a PostToolUse hook can grep the output down before Claude sees it. The official docs present this as a token-cost measure; the legibility payoff is the same mechanism used for a different reason.
Teach it the entry points in CLAUDE.md. Which command builds, which runs one test file rather than the suite, where the logs land. A narrower command produces a shorter failure, which fits in the window.
Close the CI loop
The second half of the claim is that the agent should fetch CI results itself, so the cycle is write → test → push → watch → read the failure → fix, with no human relaying screenshots of a red check.
# the shape worth allowlisting — failed-step logs only, not the whole run
gh run list --branch "$(git branch --show-current)" --limit 5
gh run view --log-failedgh run view --log-failed matters more than it looks: a full --log of a CI run is routinely megabytes, and piping that into a failing-command result is the head-and-tail problem again at ten times the scale.
Claude Code ships one turnkey version of this loop: /autofix-pr spawns a Claude Code on the web session that watches the current branch's PR and pushes fixes when CI fails or reviewers comment. It detects the PR with gh pr view, so it needs the gh CLI and web access. Use it when you want the loop to run while you are away; wire up gh directly when you want to stay in the terminal.
What is unverified here
The specific practice — deliberately reordering your own build and test output so the informative failure precedes the generic one — comes from a community transcript of a live coding session, not from Anthropic's documentation. The corpus says to give Claude a check that produces a readable pass/fail, and it documents the output limits above precisely. It says nothing about ordering your error output for an agent reader.
So the mechanism is documented and the practice is inferred from it. What would settle it is not an argument but a measurement: produce a failure whose useful line sits in the middle of a lot of noise, and see whether it survives into the transcript.
# a synthetic noisy failure: informative line buried between two walls of frames
cat > /tmp/noisy.py <<'EOF'
import sys
for i in range(600): print(" at wrapper.frame_%d (runner.js:%d)" % (i, i))
print("ERROR: type mismatch at src/auth/token.ts:42 — expected string, got number")
for i in range(600): print(" at teardown.frame_%d (runner.js:%d)" % (i, i))
sys.exit(1)
EOF
python3 /tmp/noisy.py; echo "exit=$?"Run that inside a Claude Code session, then ask Claude what the error was. If it can name src/auth/token.ts:42, the excerpt caught it. If it describes wrapper frames, you have reproduced the failure mode this tip is about — on your own machine, with your own limits — and you now know whether reordering is worth the afternoon.
Checkpoint
-
Does your test runner's failure output put the file and line in the first 40 lines, or after a banner and a wrapper trace?
Show answerAnswer
Only your own runner can answer that, so trigger a real failure and read the first screen. The bar to hold it to is the rule of thumb here: the first ~40 lines should give you the file, the line, and what was wrong. If a framework banner and a wrapper trace get there first, you are looking at the failure this tip is about — a non-zero exit gets roughly 10,000 characters as a head-and-tail excerpt, so the informative middle is precisely what vanishes.
Reordering your own build output for an agent reader is a practice inferred from a community transcript rather than documented advice, so treat it as a judgement call about your build: fix the two or three failures Claude visibly gets stuck on, not your error handling in general.
-
What happens to a 5 MB CI log when Claude fetches it — does it fit in the read-back window, or does the head-and-tail excerpt drop the failing step entirely?
Show answerAnswer
It does not fit, by orders of magnitude. The read-back window is
BASH_MAX_OUTPUT_LENGTH— default 30,000 characters, ceiling 150,000 (on v2.1.261+bashOutputMaxCharssizes the inline ceiling and the window together, up to 128,000). Which branch you land on decides how recoverable that is: a fetch that exits 0 gets ~30,000 characters inline plus a saved file path Claude can read or grep for the rest, while a failing one gets ~10,000 characters of head-and-tail and no file path at all.Either way a failing step sitting in the middle is what gets dropped, which is why
gh run view --log-failedbeats a full--log. Cut the volume rather than raising the window. -
Which
ghsubcommands are on your allowlist, and does that list also grant merge, release, or secret access you did not mean to give?Show answerAnswer
That list is yours to read out of your own settings — no one can tell you what is on it. What to look for: a blanket approval of
ghcoversgh pr merge,gh release createandgh secret setalongside log reading, so allowlist the read shapes instead (gh run view,gh run list,gh pr diff).Whatever you allow, CI logs remain untrusted input. A log can carry text addressed to the agent, and it can carry a token some job printed by accident.