Exit code 137 and the rest: reading how a process died

An exit code is one byte, and most of them are more specific than they look. Once you know that anything above 128 means a signal, the number stops being noise and starts naming the cause.

By the Continuum team. We build a workbench that runs Claude Code, Codex, and their peers, so the model rates quoted here are the ones our own cost analytics ship with.

The short version

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.

What you need to know
  • 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 256 becomes 0 and exit -1 becomes 255.

The lookup table

Start here. The right-hand column is the first thing to check, not the only thing.

CodeMeansUsual causeFirst move
0SuccessNothingNothing
1Generic failureThe program decided it failedRead its own output; the code carries no detail
2Shell misuseBad option or missing argument to a builtinCheck the flags you passed
124GNU timeout expiredThe command outlived its budgetRaise the budget, or find the hang
126Found, not executableMissing +x, or a noexec mountchmod +x
127Command not foundPATH, a typo, or a missing shebang interpretercommand -v it
130SIGINT (128+2)Someone pressed Ctrl+CNothing. That was you.
134SIGABRT (128+6)The process aborted itself, often a V8 heap OOMRead the fatal error above it
137SIGKILL (128+9)Out of memory, or a forced killCheck the OOM killer
139SIGSEGV (128+11)Segfault in native codeNative module or binary, not your script
143SIGTERM (128+15)A stop request: CI cancel, docker stop, systemdFind who asked
255Out of range, or SSH failureexit -1, or SSH could not connectIf 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.

Prove all of it in ten seconds. These are real outputs.
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 status is one byte, and everything above 128 is a signalEXIT STATUS IS ONE BYTE1 to 127128 to 2550 = successthe program, or the shella signal: subtract 128130SIGINT128+2134SIGABRT128+6137SIGKILL128+9139SIGSEGV128+11143SIGTERM128+15255 is the wrap, not a signal: exit -1 and a failed ssh both land there.

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.

CauseTellFix
Kernel OOM killerA Killed process line in dmesgLess memory, or more of it
Container memory limitOOMKilled in the runtime stateRaise the limit, or shrink the job
docker stop timing outA SIGTERM ten seconds earlierHandle SIGTERM, or raise the grace period
Explicit kill -9Nothing in the kernel logWhoever ran it knows why
A supervisor giving upA stop request first, then the killShut down faster
V8 heap exhaustionNot 137. Node aborts, so you get 134.--max-old-space-size

Find out whether memory did it

Linux. The first command answers it most of the time.
# 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 and Kubernetes ask the runtime instead of the kernel.
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

  1. Raise the limit if the workload is honest. A build that genuinely needs 6 GB in a 4 GB container is not a bug.
  2. Cap the runtime rather than the container. NODE_OPTIONS=--max-old-space-size=6144 makes Node fail loudly at 6 GB instead of letting the cgroup kill it silently.
  3. Reduce concurrency. Four parallel test workers on a 4 GB runner is the classic self-inflicted 137.
  4. Handle SIGTERM. If the 137 was a stop escalating, exiting on TERM stops the kill from happening at all.
  5. Watch it, do not guess. /usr/bin/time -v cmd on 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".

SenderDefault grace before escalation
docker stop10 seconds, then SIGKILL (so 143 becomes 137)
Kubernetes pod terminationterminationGracePeriodSeconds, 30 by default
systemctl stopTimeoutStopSec
GNU timeoutSends TERM; -k adds a later KILL
A cancelled CI jobRunner-specific, usually seconds
kill with no flagNone. TERM is the default signal.
A shell script that exits cleanly instead of being killed.
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

127126
MeansNot foundFound, not executable
TypicalTypo, or PATHMissing +x
AlsoA shebang naming an interpreter that does not existA noexec mount
Checkcommand -v thingls -l thing
The four 127s that are not typos.
# 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 timeout reporting that the command was still running when the clock ran out. 125 means timeout itself 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 ssh means SSH failed before your command ran, so the remote exit status does not exist. From anything else it is usually an exit -1 wrapping around.
Distinguishing an SSH failure from a remote command failure.
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.

CodeUsually
127PATH in a non-interactive shell, or a Node upgrade moved the npm prefix
137The box ran out of memory, often with several sessions running at once
143A CI step timed out, or the container was stopped under it
130Ctrl+C. Note that Escape interrupts a turn without killing the session.
1A normal failure: auth, a rejected prompt, or --max-turns reached
0Success, including claude auth status when signed in
A PreToolUse hook that actually blocks.
#!/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.

  1. GNU Bash manual: exit status
  2. GNU coreutils: timeout
  3. Kubernetes: debug running pods
  4. Claude Code hooks reference
Try it

Know what
is running.

Continuum lists every agent session with its live state, so you find the memory ceiling before it finds you.

free app · your subscriptions · local-first