A red build usually ends with one useless line: Process completed with exit code 1. The real cause is somewhere above it, often thousands of lines up, and the loudest error is rarely the first one.
Rule 1: the first real error beats the last line
Errors cascade. A package that fails to install makes the build fail, which makes the container step fail, which makes the job fail. If you read from the bottom you fix the symptom.
Read in this order:
- Find the step that went red (the step header just above the first error).
- Inside it, find the first line that names a problem, not the one that says "failed".
- Ask which kind of problem it is. The kind decides what to check next.
Rule 2: sort the cause into a kind
| Kind | What it looks like | Check next |
|---|---|---|
| Out of memory | heap out of memory, exit code 137 | runner memory, parallel jobs |
| Dependency | ERESOLVE, Cannot find module, No matching distribution | lockfile vs manifest, registry reachable |
| Network | ECONNRESET, Could not resolve host, 502 | status page of that host, one re-run |
| Timeout | timed out, run cancelled | duration of earlier green runs |
| Test failure | AssertionError, FAILED tests/... | run that test alone, same environment |
| Permission | 403 Forbidden, Permission denied | token scope, secret exists in this environment |
A worked example (invented log)
#9 [build 4/6] RUN npm run build #9 45.11 FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory ... ERROR: failed to solve: process "/bin/sh -c npm run build" did not complete successfully: exit code: 134 ##[error]Process completed with exit code 1. Read from the bottom, you would blame the container build. Read from the first problem, the build step simply ran out of memory. The container error is a side effect. The fix is memory (or less parallelism), not the Dockerfile.
A second example, a test that failed because the network failed:
FAIL src/api.test.js FetchError: request to api.example.test/v1/profile failed, reason: read ECONNRESET Tests: 1 failed, 4 passed, 5 total Here the test is not wrong. Re-run once before touching code. If it fails again in the same place, then look at the host.
Do not paste secrets into anything
Logs often contain tokens, bearer headers and passwords inside URLs. Mask them before pasting a log anywhere, including an AI chat.
If you want this automated
I wrote a small local tool for it (Python, standard library, reads one log file, masks credentials, changes nothing). It is called ci-failure-triage and it is a paid skill on Agensi ($5). The method above works without it.
Disclosure: I made the tool mentioned in the last section. This post was drafted by an AI assistant (Claude) and reviewed by me before publishing.
