Skip to content
Latchkey

CI Exit Codes: What Each One Actually Tells You

Exit code 137 is not a build error. It is the kernel killing your process for memory, and nothing in the application log will explain it.

CI logs surface exit codes without interpretation, and several of the common ones are not application errors at all. Codes above 128 mean the process was terminated by a signal, which points at the environment rather than the code.

The other trap is not an exit code at all: a pipeline reports the status of its last command unless pipefail is set, so a failing build piped to tee exits zero.

Codes you will actually see

CodeMeaningWhat to do
0Success
1General errorRead the log; it is a real failure
2Shell misuse, or tool-specificFrequently a bad flag or missing file
126Command found but not executableCheck the executable bit and shebang
127Command not foundNot on PATH in the runner shell
128Invalid argument to exit
130SIGINT (128 + 2)Interrupted, usually cancellation
137SIGKILL (128 + 9)Almost always the OOM killer
139SIGSEGV (128 + 11)Segfault in a native component
143SIGTERM (128 + 15)Graceful termination, often a timeout

Exit 137 is a memory problem

Terminal
# confirm it was the OOM killer
docker inspect <container> --format '{{.State.OOMKilled}} {{.State.ExitCode}}'

# see peak usage before it dies
/usr/bin/time -v <command> 2>&1 | grep "Maximum resident set size"

# common remedies
node --max-old-space-size=4096 ...   # raise the JS heap
cargo build -j 2                     # fewer parallel jobs
# or move the job to a larger runner

The zero that should not be zero

.github/workflows/ci.yml
# WITHOUT pipefail, this exits 0 even when the build fails,
# because tee succeeded
- run: npm run build | tee build.log

# WITH pipefail, the pipeline fails when any element fails
- shell: bash
  run: |
    set -euo pipefail
    npm run build | tee build.log

Tool-specific non-zero codes that are not failures

  • pytest exits 5 when zero tests were collected, which is a configuration problem, not a pass.
  • grep exits 1 when it matches nothing, which is normal and will fail a set -e script.
  • diff exits 1 when files differ, which is frequently the expected outcome.
  • terraform plan -detailed-exitcode exits 2 when changes are present, which is success.
  • Always check a tool documented exit codes before treating non-zero as an error.

Reading exit codes in CI

  • A shell reports 128 + N when a process is terminated by signal N: 137 is SIGKILL (usually the out-of-memory killer), 143 is SIGTERM, 130 is SIGINT.
  • Exit code 137 in a container almost always means the kernel killed it for memory. Nothing in the application log will explain it.
  • A pipeline reports the exit status of its LAST command unless pipefail is set, which is how a failing command piped to tee produces a green build.
  • Some tools use non-zero codes for non-failure outcomes. Check the tool documentation before treating any non-zero code as an error.

Frequently asked questions

What does exit code 137 mean in CI?
The process was killed by SIGKILL (128 + 9), essentially always by the kernel out-of-memory killer. It is not an application error and the application log will not explain it. Raise the memory limit, reduce parallelism, or use a larger runner.
What is exit code 143?
SIGTERM (128 + 15), a graceful termination request. In CI it usually means a job or step timeout, or a runner being reclaimed, rather than a fault in your code.
Why did my build pass when a command failed?
Without pipefail, a shell pipeline exits with the status of its last command. failing-command | tee log exits zero because tee succeeded. Add set -euo pipefail to any multi-line run block.
Is a non-zero exit code always a failure?
No. grep exits 1 on no match, diff exits 1 on differences, pytest exits 5 when no tests were collected, and terraform plan -detailed-exitcode exits 2 when changes exist. Check the tool documentation before treating non-zero as an error.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card