Why is my program terminating with exit code 3?

There is no standard for process exit codes. You can pass anything you want to Exit­Process, and that’s what Get­Exit­Code­Process will give back. The kernel does no interpretation of the value. If youw want code 42 to mean “Something infinitely improbable has occurred” then more power to you.

There is a convention, however, that an exit code of zero means success (though what constitutes “success” is left to the discretion of the author of the program) and a nonzero exit code means failure (again, with details left to the discretion of the programmer). Often, higher values for the exit code indicate more severe types of failure. The command processor ERROR­LEVEL keyword was designed with these convention in mind.

There are cases where your process will get in such a bad state that a component will take it upon itself to terminate the process. For example, if a process cannot locate the DLLs it imports from, or one of those DLLs fails to initialize, the loader will terminate the process and use the status code as the process exit code. I believe that when a program crashes due to an unhandled exception, the exception code is used as the exit code.

A customer was seeing their program crash with an exit code of 3 and couldn’t figure out where it was coming from. They never use that exit code in their program. Eventually, the source of the magic number 3 was identified: The C runtime abort function terminates the process with exit code 3.

Comments (20)
  1. Skyborne says:

    I was once on a platform where a failed assert() called abort(), which did its job by issuing an illegal instruction to the CPU.  I was expecting lots of output from my program and redirecting it all to a file, but the shell was helpfully reporting that my program died of an illegal instruction.  It took several rounds of adding printf()s and wondering why I wasn't seeing any output to remember that I was capturing it.  Derp!

    (This being 10 years ago, details may not be 100% accurate.  Please take with the necessary amount of salt.)

  2. Peter Smith says:

    A good convention is that error codes should always be positive.  Why?  Because error code handling is often via the "IF ERRORLEVEL N".  The IF ERRORLEVEL N command test to see if the value is greater than or equal to; it doesn't do "if not zero" very easily.

    Link to ERRORLEVE: support.microsoft.com/…/69576)  

  3. Adam Rosenfield says:

    Portable software should always only use exit statuses 0 through 127 inclusive (and really portable software should only use the macros EXIT_SUCCESS and EXIT_FAILURE).  On POSIX systems, only the lower 8 bits of the exit status are retained, and exit statuses 128-255 are used for abnormal process termination (segfaults, abort(), etc.).  So, normal process termination via exit() or returning from main should only use statuses 0-127.

    You can then tell exactly how a process exited using macros such as WIFEXITED, WIFSIGNALED, etc. (pubs.opengroup.org/…/wait.html).  There's no possibility of confusing normal termination with status 3 with abnormal termination via abort().

  4. Joshua says:

    A thing that bit me once is an unhandled exception uses its code as the exit code, and most managed exceptions have negative codes. This breaks IF ERRORLEVEL 1 and caused me quite a headache until I figured it out.

  5. Cheong says:

    @Joshua: You're right. I'm also accustomed to this convention.

    Errors have negative return value. When program state is success but has something it wants to tell, use a positive value.

  6. Adrian says:

    @Adam Rosenfield: And if you're trying to be really, REALLY portable (while writing in C or C++), you shouldn't return EXIT_SUCCESS or EXIT_FAILURE from main().  Instead, you should call exit() with one of those values.

    Returning from main generally causes the value to be passed back to the OS without any interpretation.  Calling exit(), however, will generally translate the EXIT_SUCCESS and EXIT_FAILURE values to corresponding values OS.

    For example, on VAX/VMS, even values are failures, and odd values are successes.  So returning EXIT_SUCCESS, which is typically defined to be 0, causes an apparent crash on exit.

  7. John says:

    You guys are way off.  Error code 3 on Windows is ERROR_PATH_NOT_FOUND.  Clearly the program could not find the correct code path to take.

  8. Adam Rosenfield says:

    @Adrian: No, returning from main is entirely equivalent to calling exit.  C90 §

    "A return from the initial call to the main function is equivalent

    to calling the exit function with the value returned by the main

    function as its argument.  If the main function executes a return that

    specifies no value, the termination status returned to the host

    environment is undefined."

    Or the more recent C99 §

    "If the return type of the main function is a type compatible with int, a return from the

    initial call to the main function is equivalent to calling the exit function with the value

    returned by the main function as its argument;10) reaching the } that terminates the

    main function returns a value of 0. If the return type is not compatible with int, the

    termination status returned to the host environment is unspecified."

    That behavior you describe on VAX/VMS was likely pre-ANSI C where the language was not standardized.

  9. anon says:

    Adrian said "portable" not "standards compliant"

  10. jim steele says:

    Clearly, the number three refers to the number of rank insignia on Commander Riker's uniform.

  11. Joshua Ganes says:

    @jim steele – I don't know which is worse — that you made that reference, or that I know exactly what you're talking about.

  12. Evan says:

    @jim steele

    That was fantastic. I'm still chuckling to myself a couple minutes later.

  13. S says:

    But… Riker was "number one"??

  14. Jules says:

    @Adam: "reaching the } that terminates the main function returns a value of 0"

    I've worked with an awfully large number of C compilers that don't comply with this requirement.

  15. Medinoc says:

    That said, it's an awful requirement from a purist standpoint, where a function whose return type is int should always have an explicit return statement.

  16. Cesar says:

    3 is an awful value for abort(), because it can be very easily confused with a legitimate exit code (as happened with that customer).

    I did a quick test on a Linux machine, and the value it returns is much better: 134. This is much harder to confuse with a legitimate exit code. It is also a better value if you follow that the "higher value indicates more severe failure" rule of thumb.

    (Why 134? In hex it is 0x86, and SIGABRT is 6, so I can guess it is simply the signal number with a high bit set.)

  17. JM says:

    @Jules: that's because it's a recent addition. It's a C++-ism that was backported to C99. C89 says: "if the main function executes a return that specifies no value, the termination status returned to the host environment is undefined" ( and "reaching the } that terminates a function is equivalent to executing a return statement without an expression" ( Not returning anything from main() is "merely" undefined behavior in C89, which of course compilers don't need to do anything about.

  18. Adam Rosenfield says:

    @Cesar: Yes, see my first comment and the documentation for wait(2).  Processes terminated with a signal are given the exit status (128 + signal#).

  19. K&R says:

    In *pure* K&R C a return type of int was assumed for functions which didn't specify any explicit type.

  20. Random832 says:

    Actually, that's the shell that populates "$?" with 128+signal. The actual value given by wait(2) is 16 bits and fully distinguishes between an 8-bit exit status and a signal termination, along with additional information to represent suspend/continue and a flag for whether the core was dumped. It's still a good idea to avoid exit statuses of 128 or above, so that shell scripts can make a distinction.

Comments are closed.