Exit code 137 is 128 plus signal 9, SIGKILL: something killed the process outright, and on Linux and in containers that is almost always the out-of-memory killer. 143 is 128 plus 15, SIGTERM, a polite shutdown request. 127 means the command was not found, 126 means it was found but could not be executed, 130 is Ctrl+C, 124 is a GNU timeout expiring, 1 is a generic failure, 2 is shell misuse, and 255 is an out-of-range status or an SSH connection failure.
- Anything above 128 is a signal: subtract 128 to get the signal number.
- 137 = SIGKILL. On Linux and in containers, read that as out of memory first.
- 143 = SIGTERM. Something asked the process to stop, and it did.
- 127 is not found, 126 is found but not runnable. Different fixes.
- Exit status is one byte.
exit 256becomes 0 andexit -1becomes 255.
The lookup table
Start here. The right-hand column is the first thing to check, not the only thing.
| Code | Means | Usual cause | First move |
|---|---|---|---|
0 | Success | Nothing | Nothing |
1 | Generic failure | The program decided it failed | Read its own output; the code carries no detail |
2 | Shell misuse | Bad option or missing argument to a builtin | Check the flags you passed |
124 | GNU timeout expired | The command outlived its budget | Raise the budget, or find the hang |
126 | Found, not executable | Missing +x, or a noexec mount | chmod +x |
127 | Command not found | PATH, a typo, or a missing shebang interpreter | command -v it |
130 | SIGINT (128+2) | Someone pressed Ctrl+C | Nothing. That was you. |
134 | SIGABRT (128+6) | The process aborted itself, often a V8 heap OOM | Read the fatal error above it |
137 | SIGKILL (128+9) | Out of memory, or a forced kill | Check the OOM killer |
139 | SIGSEGV (128+11) | Segfault in native code | Native module or binary, not your script |
143 | SIGTERM (128+15) | A stop request: CI cancel, docker stop, systemd | Find who asked |
255 | Out of range, or SSH failure | exit -1, or SSH could not connect | If SSH, it never reached the remote command |
Why the numbers look like that
An exit status is a single byte, so the range is 0 to 255 and everything outside it wraps. That is the whole reason 255 shows up so often as "something went wrong": it is what a negative status becomes.
bash -c "exit 0"; echo "$?" # 0
bash -c "exit 256"; echo "$?" # 0 <- wrapped
bash -c "exit -1"; echo "$?" # 255 <- wrapped
nosuchcommand; echo "$?" # 127
chmod -x s.sh; ./s.sh; echo "$?" # 126
bash -c "sleep 30 & kill -9 \$!; wait \$!"; echo "$?" # 137
timeout 1 sleep 5; echo "$?" # 124
- 0 to 125 belong to the program. It picks what they mean, and most programs only ever use 0 and 1.
- 126 and 127 belong to the shell. It could not run the thing you asked for.
- 128 + N means a fatal signal N ended the process.
- 255 is the wrap-around, and SSH claims it for connection failures.
Exit code 137: something sent SIGKILL
SIGKILL cannot be caught, blocked, or handled. The process got no chance to flush a log, print a stack, or say goodbye, which is exactly why 137 arrives with no explanation attached. Somebody or something killed it.
Causes, in order of how often they turn out to be the answer.
| Cause | Tell | Fix |
|---|---|---|
| Kernel OOM killer | A Killed process line in dmesg | Less memory, or more of it |
| Container memory limit | OOMKilled in the runtime state | Raise the limit, or shrink the job |
docker stop timing out | A SIGTERM ten seconds earlier | Handle SIGTERM, or raise the grace period |
Explicit kill -9 | Nothing in the kernel log | Whoever ran it knows why |
| A supervisor giving up | A stop request first, then the kill | Shut down faster |
| V8 heap exhaustion | Not 137. Node aborts, so you get 134. | --max-old-space-size |
Find out whether memory did it
# did the kernel kill something?
dmesg -T | grep -iE "killed process|out of memory"
journalctl -k --since "30 min ago" | grep -i oom
# cgroup v2: a non-zero oom_kill means the LIMIT did it, not the host
cat /sys/fs/cgroup/memory.events
cat /sys/fs/cgroup/memory.max
docker inspect --format "{{.State.OOMKilled}} {{.State.ExitCode}}" my-container
kubectl get pod my-pod \
-o jsonpath="{.status.containerStatuses[0].lastState.terminated.reason}"
# OOMKilled
kubectl describe pod my-pod | grep -A4 "Last State"
Fixes, cheapest first
- Raise the limit if the workload is honest. A build that genuinely needs 6 GB in a 4 GB container is not a bug.
- Cap the runtime rather than the container.
NODE_OPTIONS=--max-old-space-size=6144makes Node fail loudly at 6 GB instead of letting the cgroup kill it silently. - Reduce concurrency. Four parallel test workers on a 4 GB runner is the classic self-inflicted 137.
- Handle SIGTERM. If the 137 was a stop escalating, exiting on TERM stops the kill from happening at all.
- Watch it, do not guess.
/usr/bin/time -v cmdon Linux prints the peak resident set, which ends the argument.
Exit code 143: something asked first
143 is the polite version of 137. SIGTERM is catchable, so the process had the option to shut down cleanly and either took it or was cut off later. The useful question is never "what is 143", it is "who sent it".
| Sender | Default grace before escalation |
|---|---|
docker stop | 10 seconds, then SIGKILL (so 143 becomes 137) |
| Kubernetes pod termination | terminationGracePeriodSeconds, 30 by default |
systemctl stop | TimeoutStopSec |
GNU timeout | Sends TERM; -k adds a later KILL |
| A cancelled CI job | Runner-specific, usually seconds |
kill with no flag | None. TERM is the default signal. |
cleanup() { echo "stopping"; kill -TERM "$child" 2>/dev/null; wait "$child"; exit 143; }
trap cleanup TERM INT
long_running_thing & child=$!
wait "$child"
Exit codes 127 and 126: the shell could not run it
| 127 | 126 | |
|---|---|---|
| Means | Not found | Found, not executable |
| Typical | Typo, or PATH | Missing +x |
| Also | A shebang naming an interpreter that does not exist | A noexec mount |
| Check | command -v thing | ls -l thing |
# 1. PATH differs in a non-interactive shell
ssh host "echo \$PATH" # not what your login shell shows
# 2. the shebang interpreter is missing
head -1 script.sh # #!/usr/bin/env python3 ... is it installed?
# 3. CRLF line endings: the interpreter is literally "bash\r"
file script.sh # ... with CRLF line terminators
sed -i "s/\r$//" script.sh
# 4. the container image never had the binary
docker run --rm my-image which claude
The rest: 1, 2, 124, 130, and 255
- 1 carries no information beyond "failed". The program chose it. Read its stderr; the number will never tell you more.
- 2 from a shell builtin means incorrect usage, usually an invalid option or a missing argument. Many CLI tools copy the convention.
- 124 is GNU
timeoutreporting that the command was still running when the clock ran out.125meanstimeoutitself failed. - 130 is Ctrl+C. In a CI log it usually means the runner forwarded an interrupt, not that a human was there.
- 255 from
sshmeans SSH failed before your command ran, so the remote exit status does not exist. From anything else it is usually anexit -1wrapping around.
ssh host "exit 42"; echo "$?" # 42 -> the remote command ran and failed
ssh nosuchhost true; echo "$?" # 255 -> SSH never got there
# force the difference in a script
ssh -o BatchMode=yes -o ConnectTimeout=5 host "true" || echo "connection problem"
When the process that died was an agent
Coding agents produce these codes for boring reasons and for one interesting one. The boring reasons first.
What each code usually means when claude or codex is the process.
| Code | Usually |
|---|---|
127 | PATH in a non-interactive shell, or a Node upgrade moved the npm prefix |
137 | The box ran out of memory, often with several sessions running at once |
143 | A CI step timed out, or the container was stopped under it |
130 | Ctrl+C. Note that Escape interrupts a turn without killing the session. |
1 | A normal failure: auth, a rejected prompt, or --max-turns reached |
0 | Success, including claude auth status when signed in |
#!/usr/bin/env bash
# .claude/hooks/no-force-push.sh
read -r payload
if echo "$payload" | grep -q "push --force"; then
echo "Force pushing is not allowed on this repo." >&2
exit 2 # 2 blocks. 1 would NOT.
fi
exit 0
Questions people ask
The process was killed by SIGKILL, signal 9, because 137 is 128 plus 9. On Linux and in containers that is almost always the out-of-memory killer or a container memory limit. SIGKILL cannot be caught, so the process printed nothing on its way out.
No. It means SIGKILL specifically, which anything can send. A docker stop that times out escalates to SIGKILL, and a plain kill -9 produces the same code. Check dmesg or the container runtime state before assuming memory.
143 is SIGTERM, a request to stop that the process can catch and handle. 137 is SIGKILL, which it cannot. A 143 followed by a 137 usually means a graceful shutdown ran out of time and was forced.
The shell could not find the command. Either it is not installed, the PATH in that shell does not include it, there is a typo, or a script shebang names an interpreter that does not exist. 126 is the related case where the file was found but could not be executed.
Because it does not have to. Codes 0 to 125 belong to the program, and most programs only ever use 0 for success and 1 for everything else. The detail is in stderr, not in the number.
Usually an out-of-range status wrapping around, since exit -1 becomes 255 in an 8-bit exit status. From ssh it means the connection itself failed, so your remote command never ran and its status does not exist.
130, which is 128 plus signal 2, SIGINT. Seeing it in a CI log normally means the runner forwarded an interrupt when cancelling the job.
Hooks use exit 2 as the blocking code, not 1. Exit 0 is success, exit 2 blocks and feeds stderr back to Claude as the reason, and every other non-zero status is a non-blocking error that lets the action proceed.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.