I have a question about bash exit codes. Can I basically define what each code I return means? If so is there some standard way of defining or documenting that?

I've been looking into it and I see there are various lists of "common" exit codes out there. For example

I also looked at some GNU docs and its kind of confusing.

Exit statuses fall between 0 and 255, though, as explained below, the shell may use values above 125 specially.

I'm just curious if there are any kind of standard practices around it.

  • 1
    Obligatory: xkcd.com/927
    – jesse_b
    Aug 19, 2022 at 17:40
  • 4
    The only "standard" that I would consider universal is that 0 is success and anything other than 0 is failure. Although that also varies across languages.
    – jesse_b
    Aug 19, 2022 at 17:41
  • 2
    That reference is very mistaken. The huge list of "exit codes" it shows (0 to 133) is actually the values of errno returned by various functions within the GNU C libraries. There is no logical connection between these error codes and any process exit convention. Furthermore, this list overlaps the range which the article itself specifies as used by the shell (120 to 127), and also overlaps the values returned when a process is terminated by signals (e.g. 130 for SIGINT). In practice, I believe no standard utility returns an exit code above 5. Aug 19, 2022 at 17:53
  • @Paul_Pedant: what do you mean by standard utility there? Because that seems off to me.
    – jesse_b
    Aug 19, 2022 at 17:59
  • 1
    @Paul_Pedant: curl uses exit codes 0 through 99. wget uses 0 through 8.
    – jesse_b
    Aug 20, 2022 at 12:40

2 Answers 2


The codes above are mostly nonsense. And this is not formal definitions. I found another one in Advanced Bash-Scripting Guide

Exit Code Number
1   Catchall for general errors
2   Misuse of shell builtins (according to Bash documentation)
126 Command invoked cannot execute
127 "command not found"
128 Invalid argument to exit
128+n   Fatal error signal "n"
130 Script terminated by Control-C
255*    Exit status out of range

But these codes are recommendations so feel free to use them for your purpose.

  • 1
    Thanks Romeo, this is the feeling I got. Aug 19, 2022 at 18:32

The status is a 16-bit word (something like JSON, but for the 1970s where a 16-bit register was a rare commodity, especially after something complicated like fork) wherein various bits or ranges of bits have particular meanings. Perl puts the 16-bit word set by a wait(2) call into the $? variable, and we can find the particular exit code by shifting the upper bits over, because that is where that field is in the word:

$ perl -e 'fork || exit 42; wait; printf "%016b\n", $?'
$ perl -e 'fork || exit 42; wait; printf "%016b\n", $? >> 8'
$ perl -e 'fork || exit 42; wait; printf "%d\n", $? >> 8'

Unix shells mangle the 16-bit status word into a different form:

$ perl -e 'exit 42'; echo $?

which becomes relevant if there was a core dump (there's a flag for that in the 16-bit word) or a signal:

$ perl -MPOSIX -e 'raise SIGTERM'; echo $?
$ echo $((143-128))
$ kill -l | grep 15
15   TERM Terminated                    31   USR2 User defined signal 2

So that's the signal number plus 128 (the shell mangling the value) which in the actual 16-bit exit status word instead appears as:

$ perl -MPOSIX -e 'fork || raise SIGTERM; wait; printf "%016b\n", $?'
$ perl -E 'say 0b1111'

So Perl is using the 16-bit word returned by a wait(2) call directly, while the Unix shell mangles it into some other form. Depending on the language you are using, you'll need to either follow the C convention and do things like "left shift to get the exit code" (there are often macros or other such calls to assist with this, check the fine documentation for details), or "subtract 128 to find the signal number" if using the shell mangled value.

For extra points, create a program that creates a core file and see what the exit status word looks like, or try different signal numbers, etc.

How To Use The Status Word

There are various conventions which may or may not be frowned upon, e.g. the exit codes from sysexits.h; some of these codes are important to Email delivery systems (by way of sendmail, historically) where particular codes can be used to signal a "do try again" temporary failure (which becomes a 4xx code in SMTP), as opposed to a "reject and bounce that email" (5xx) which you may see references to in mail delivery agent software such as procmail or similar.

$ grep EX_TEMPFAIL /usr/include/sysexits.h
 *      EX_TEMPFAIL -- temporary failure, indicating something that
#define EX_TEMPFAIL     75      /* temp failure; user is invited to retry */

Otherwise, 0 usually means "okay" (but programs may fail without setting a non-zero code, buyer beware!) and anything not-0 means "not okay" (but what exactly that means varies, it could be a temporary failure, noise to ignore, something really really bad, etc). There could be site or vendor or software specific meanings for specific exit codes; these particular codes may or may not be documented. A man page may commonly have a generic .Ex -std macro to generate a blurb like

     The foo utility exits 0 on success, and >0 if an error occurs.

or more specific documentation as need be, if the software is well documented. A lot of software isn't so well documented...

When Is This Important

Monitoring or test suits probably do need to distinguish between "any old non-zero exit" and a program suddenly instead exiting with a segfault or coredump, or perhaps a segfault is the expected value for a particular test, and it would be unusual for that not to happen. Monitoring or tests that only report 0-or-not will omit valuable debugging information.

SMTP servers, as mentioned above, will perform different actions depending on the precise exit status word returned by a local delivery agent. A hard bounce is very different from a temporary delivery failure that the server will likely try again.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .