How exit codes work
Every process ends with an 8-bit status, 0 to 255. By convention 0 means success and anything else failure; the shell exposes it as $?, and &&, ||, if and set -e all branch on it. The number's meaning beyond zero/non-zero belongs to the program, with a few conventions that are nearly universal.
The shell's own codes
- 126: the file was found but could not be executed. Missing execute bit, a directory, a binary for another architecture, or a
noexecmount. - 127: not found. A typo, a program not installed, or a
PATHthat differs between your shell and the environment the script runs in (cron, systemd, Docker). Also a shebang line pointing at a missing interpreter, including\r\nline endings making/bin/bash\r. - 128 + n: the process died from signal n. The shell cannot return a signal directly, so it adds 128. 130 is Ctrl-C (SIGINT 2), 137 is SIGKILL (9), 139 is SIGSEGV (11), 143 is SIGTERM (15). The signals reference lists them all.
137 in containers
Exit 137 from a container is almost always the kernel's OOM killer or the container runtime enforcing a memory limit: the process was sent SIGKILL, which cannot be caught, so nothing was logged by the application. Check docker inspect for OOMKilled: true, dmesg for “Out of memory”, or Kubernetes events for OOMKilled. 143 is the normal result of docker stop or a pod termination when the process handled SIGTERM; 137 after a stop means it did not shut down within the grace period.
sysexits.h
BSD defined codes 64–78 for mail and system tools to report categories of failure (EX_USAGE 64, EX_NOINPUT 66, EX_TEMPFAIL 75…). Postfix, sendmail and some Perl and Go programs still use them; nothing else does, so treat them as a convention, not a rule.
Questions
What does exit code 1 mean?
A generic failure: the program decided something went wrong and did not have a more specific code. For grep it means no lines matched, which is not an error at all. Read the program’s stderr output, not the number.
Why do I get 127 in cron or systemd but not in my shell?
The PATH is different. Your shell’s profile adds directories such as /usr/local/bin or ~/.local/bin that cron and systemd do not see. Use absolute paths or set PATH at the top of the script.
What is the maximum exit code?
255. exit 256 wraps to 0, exit 300 becomes 44 (300 mod 256). Negative values wrap too: exit(-1) is 255.
How do I get the exit code of a pipeline?
By default $? is the last command’s status. set -o pipefail makes the pipeline fail if any stage fails, and the PIPESTATUS array in bash holds every stage’s code.