Error levels
I should have done this a long time ago. I'm going to start allocating standard error levels for my own use.
To be clear, these are process error levels, also known as exit status and error code. I endeavour to allocate particular error levels for my own use in the range [10, 100), comfortably in two-digit territory.
Note that I mostly program in PHP, so 'error', 'exception', and 'assertion' are particular to PHP, but they might apply in other contexts.
Note: in the following tables I indicate a corresponding HTTP response code with similar semantics.
Major error levels
The major error levels are:
- EXIT_BAD_DATA (input error)
- EXIT_BAD_COMMAND (user error)
- EXIT_BAD_ENVIRONMENT (environment error)
- EXIT_BAD_PROGRAM (program error)
You can always use one of the major error levels, but a more specific code is usually better if possible.
My error levels
These are some notes about error levels specifically allocated by me.
Error Level | Category | Constant | HTTP | Meaning | Alternative |
---|---|---|---|---|---|
10 | logic exit | EXIT_CANNOT_CONTINUE | 202 | cannot continue; but nothing is abnormal or wrong | consider EXIT_SPECIAL_SUCCESS |
20 | input error | EXIT_BAD_DATA | 404 | there was a problem with inputs | |
21 | input error | EXIT_BAD_FORMAT | 404 | data was in an invalid format | |
22 | input error | EXIT_BAD_VALUE | 404 | data nominated an invalid value (but was in correct format) | |
30 | user error | EXIT_BAD_COMMAND | 400 | invalid command-line or options | |
31 | user error | EXIT_NO_FILE | 400 | a required file/directory path was not nominated | |
32 | user error | EXIT_WRONG_FILE | 400 | user nominated file (or directory) is missing | use EXIT_FILE_MISSING for system files |
33 | user error | EXIT_BAD_FILE | 403 | user nominated file (or directory) cannot be accessed due to invalid permissions | use EXIT_NO_ACCESS for system files |
34 | user error | EXIT_USER_CANCEL | 403 | user canceled | |
40 | environment error | EXIT_BAD_ENVIRONMENT | 503 | invalid run-time environment; cannot run | |
41 | environment error | EXIT_FILE_MISSING | 503 | a file (or directory) that is expected to always be available is not available | use EXIT_WRONG_FILE for user nominated files |
42 | environment error | EXIT_NO_ACCESS | 503 | a file (or directory) that should be accessible cannot be accessed due to invalid permissions | use EXIT_BAD_FILE for user nominated files |
43 | environment error | EXIT_BAD_CONFIG | 503 | config file missing or invalid | |
44 | environment error | EXIT_NO_LOCK | 503 | could not acquire lock | |
56 | environment error | EXIT_NO_SERVICE | 503 | cannot establish connection to a required service | prefer EXIT_NO_DATABASE for database services |
57 | environment error | EXIT_NO_DATABASE | 503 | cannot establish connection to a required database | |
58 | environment error | EXIT_EXHAUSTED | 507 | resources exhausted; out of memory, disk space, inodes, etc | |
59 | environment error | EXIT_OFFLINE | 503 | system offline; as configured by administrator | |
70 | program error | EXIT_BAD_PROGRAM | 500 | an unhandled and fatal situation encountered | |
71 | program error | EXIT_ERROR | 500 | an error caused process termination | |
72 | program error | EXIT_EXCEPTION | 500 | an unhandled exception caused process termination | |
73 | program error | EXIT_ASSERT | 500 | an assertion violation caused process termination | |
74 | program error | EXIT_TEST_FAILED | 500 | test failed; unit-test did not succeed | |
75 | program error | EXIT_INVALID | 500 | invalid error level; the programmer nominated an invalid error level and the host exited with 85 instead | |
80 | program error | EXIT_NOT_IMPLEMENTED | 500 | functionality not implemented | |
81 | program error | EXIT_NOT_SUPPORTED | 500 | situation not supported | |
82 | program error | EXIT_NOT_POSSIBLE | 500 | situation is supposed to be impossible | |
88 | program error | EXIT_DEBUG | 500 | programmer exited for debugging purposes | |
89 | program error | EXIT_ABORT | 500 | programmer aborted with error message | |
90 | special purpose | EXIT_SPECIAL_SUCCESS | 202 | special operation successful; used for safety (in case improperly invoked) | |
98 | special purpose | EXIT_OPTIONS_LISTED | 200 | program options listed; use when programs can be invoked to list their options in a machine readable format | |
99 | special purpose | EXIT_HELP | 200 | help or version number requested |
Other error levels
These are some notes about how other software uses error levels.
Error Level | Convention | Category | Constant | HTTP | Meaning |
---|---|---|---|---|---|
0 | success | EXIT_SUCCESS | 200 | success! | |
1 | generic | EXIT_GENERIC_1 | 404 | the generic "some problem occurred" value, prefer something more specific | |
2 | generic | EXIT_GENERIC_2 | 404 | incorrect usage or invalid arguments, the 404 of error levels | |
126 | UNIX | UNIX | EXIT_UNIX_BAD_PERMISSION | 403 | command was found but could not be executed due to permissions |
127 | UNIX | UNIX | EXIT_UNIX_BAD_COMMAND | 404 | command not found or could not be executed |
128 | UNIX | UNIX | EXIT_TERMINATED_BY_SIGNAL | 500 | process terminated by signal |
130 | UNIX | UNIX | EXIT_TERMINATED_BY_CTRLC | 500 | process terminated by Ctrl+C |
255 | generic | EXIT_GENERIC_255 | 404 | the other generic "some problem occurred" value, prefer something more specific |
Error level range
The errors are in increasing order of should-not-happenness. Basically:
- logic exit: cannot continue now, but nothing is wrong
- input error: invalid data
- user error: invalid options
- environment error: an environment problem, cannot connect to service or incompatible versions etc
- program error: shouldn't happen, a situation the code hasn't handled yet
Whether it's an input error, user error, or environment error is basically a decision for you to make. It's not super important which number you allocate, especially when multiple categories might apply. It's probably best if you try to blame the program or environment more and the user less.
Note that special purpose "success" codes are only used in situations where you want to protect the user from making a mistake. So if a version number is printed because there was a --version command-line argument; or help was printed because there was a --help command-line argument; or if a machine-readable list of options was requested and supplied; then we exit with a non-zero error level even though technically we haven't "failed", we just might have done something by accident that the use didn't expect or intend. The main reason for these options is that when you're running in bash with `set -e` enabled (and usually you should be for most non-interactive situations) then your script will stop if a process returns an error. So we return an error just in case we did something in a script that a user didn't intend. After all if they think they've successfully invoked a command, but all they've actually done is report the version number or displayed the program help text, they'd probably like to know!
Range | Category | Meaning |
---|---|---|
1-9 | not much meaning, prefer don't use | |
10-19 | logic exit | cannot continue now, but nothing is abnormal or wrong |
20-29 | input error | invalid data |
30-39 | user error | invalid command-line or options |
40-59 | environment error | improper run-time environment |
70-89 | program error | should not happen, unsupported situation |
90-99 | special purpose | success indicators, used for safety |
100+ | some have meanings in UNIX, otherwise don't use |
BASH code
#!/bin/bash # 2024-02-09 jj5 - SEE: https://www.jj5.net/wiki/error_levels LX_EXIT_SUCCESS=0 # success! LX_EXIT_CANNOT_CONTINUE=10 # logic exit: cannot continue; but nothing is abnormal or wrong: consider EXIT_SPECIAL_SUCCESS LX_EXIT_BAD_DATA=20 # input error: there was a problem with inputs LX_EXIT_BAD_FORMAT=21 # input error: data was in an invalid format LX_EXIT_BAD_VALUE=22 # input error: data nominated an invalid value (but was in correct format) LX_EXIT_BAD_COMMAND=30 # user error: invalid command-line or options LX_EXIT_NO_FILE=31 # user error: a required file/directory path not nominated LX_EXIT_WRONG_FILE=32 # user error: user nominated file (or directory) missing; use EXIT_FILE_MISSING for system files LX_EXIT_BAD_FILE=33 # user error: user nominated file (or directory) cannot be accessed due to invalid permissions: use EXIT_NO_ACCESS for system files LX_EXIT_USER_CANCEL=34 # user error: user canceled LX_EXIT_BAD_ENVIRONMENT=40 # environment error: invalid run-time environment; cannot run LX_EXIT_FILE_MISSING=41 # environment error: a file (or directory) that is expected to always be available is not available: use EXIT_WRONG_FILE for user nominated files LX_EXIT_NO_ACCESS=42 # environment error: a file (or directory) that should be accessible cannot be accessed due to invalid permissions: use EXIT_BAD_FILE for user nominated files LX_EXIT_BAD_CONFIG=43 # environment error: invalid configuration; config file missing or invalid LX_EXIT_NO_LOCK=44 # environment error: could not acquire lock LX_EXIT_NO_SERVICE=56 # environment error: cannot establish connection to a required service: prefer EXIT_NO_DATABASE for database services LX_EXIT_NO_DATABASE=57 # environment error: cannot establish connection to a required database LX_EXIT_EXHAUSTED=58 # environment error: resources exhausted; out of memory, disk space, inodes, etc LX_EXIT_OFFLINE=59 # environment error: system offline; as configured by administrator LX_EXIT_BAD_PROGRAM=70 # program error: an unhandled and fatal situation encountered LX_EXIT_ERROR=71 # program error: an error caused process termination LX_EXIT_EXCEPTION=72 # program error: an unhandled exception caused process termination LX_EXIT_ASSERT=73 # program error: an assertion violation caused process termination LX_EXIT_TEST_FAILED=74 # program error: test failed; unit-test did not succeed LX_EXIT_INVALID=75 # program error: invalid error level; the programmer nominated an invalid error level and the host exited with 85 instead LX_EXIT_NOT_IMPLEMENTED=80 # program error: functionality not implemented LX_EXIT_NOT_SUPPORTED=81 # program error: situation not supported LX_EXIT_NOT_POSSIBLE=82 # program error: situation is supposed to be impossible LX_EXIT_DEBUG=88 # program error: programmer exited for debugging purposes LX_EXIT_ABORT=89 # program error: programmer aborted with error message LX_EXIT_SPECIAL_SUCCESS=90 # special purpose: special operation successful; used for safety (in case improperly invoked) LX_EXIT_OPTIONS_LISTED=98 # special purpose: program options listed; use when programs can be invoked to list their options in a machine readable format LX_EXIT_HELP=99 # special purpose: help or version number requested LX_EXIT_UNIX_BAD_PERMISSION=126 # UNIX error: command was found but could not be executed due to permissions LX_EXIT_UNIX_BAD_COMMAND=127 # UNIX error: command not found or could not be executed LX_EXIT_TERMINATED_BY_SIGNAL=128 # UNIX error: process terminated by signal LX_EXIT_TERMINATED_BY_CTRLC=130 # UNIX error: process terminated by Ctrl+C LX_EXIT_GENERIC_1=1 # general error: error during processing LX_EXIT_GENERIC_2=2 # general error: error during processing LX_EXIT_GENERIC_255=255 # general error: error during processing
PHP code
<?php // 2024-02-09 jj5 - SEE: https://www.jj5.net/wiki/error_levels define( 'LX_EXIT_SUCCESS', 0 ); define( 'LX_EXIT_CANNOT_CONTINUE', 10 ); define( 'LX_EXIT_BAD_DATA', 20 ); define( 'LX_EXIT_BAD_FORMAT', 21 ); define( 'LX_EXIT_BAD_VALUE', 22 ); define( 'LX_EXIT_BAD_COMMAND', 30 ); define( 'LX_EXIT_NO_FILE', 31 ); define( 'LX_EXIT_WRONG_FILE', 32 ); define( 'LX_EXIT_BAD_FILE', 33 ); define( 'LX_EXIT_USER_CANCEL', 34 ); define( 'LX_EXIT_BAD_ENVIRONMENT', 40 ); define( 'LX_EXIT_FILE_MISSING', 41 ); define( 'LX_EXIT_NO_ACCESS', 42 ); define( 'LX_EXIT_BAD_CONFIG', 43 ); define( 'LX_EXIT_NO_LOCK', 44 ); define( 'LX_EXIT_NO_SERVICE', 56 ); define( 'LX_EXIT_NO_DATABASE', 57 ); define( 'LX_EXIT_EXHAUSTED', 58 ); define( 'LX_EXIT_OFFLINE', 59 ); define( 'LX_EXIT_BAD_PROGRAM', 70 ); define( 'LX_EXIT_ERROR', 71 ); define( 'LX_EXIT_EXCEPTION', 72 ); define( 'LX_EXIT_ASSERT', 73 ); define( 'LX_EXIT_TEST_FAILED', 74 ); define( 'LX_EXIT_INVALID', 75 ); define( 'LX_EXIT_NOT_IMPLEMENTED', 80 ); define( 'LX_EXIT_NOT_SUPPORTED', 81 ); define( 'LX_EXIT_NOT_POSSIBLE', 82 ); define( 'LX_EXIT_DEBUG', 88 ); define( 'LX_EXIT_ABORT', 89 ); define( 'LX_EXIT_SPECIAL_SUCCESS', 90 ); define( 'LX_EXIT_OPTIONS_LISTED', 98 ); define( 'LX_EXIT_HELP', 99 ); define( 'LX_EXIT_UNIX_BAD_PERMISSION', 126 ); define( 'LX_EXIT_UNIX_BAD_COMMAND', 127 ); define( 'LX_EXIT_TERMINATED_BY_SIGNAL', 128 ); define( 'LX_EXIT_TERMINATED_BY_CTRLC', 130 ); define( 'LX_EXIT_GENERIC_1', 1 ); define( 'LX_EXIT_GENERIC_2', 2 ); define( 'LX_EXIT_GENERIC_255', 255 ); function my_exit( int|string|Throwable $argument = EXIT_SUCCESS, bool $print_error = true, bool|null $print_hint = null ) { // if $argument is an int it is treated as an error code // if $argument is a string it is treated as an error message and EXIT_ABORT is used // if $argument is some type of Throwable an appropriate error level is determined // if $print_error is true an error message is logged // if $print_hint is true a hint for the programmer concerning other possibly related codes is printed // the DEBUG constant can influence the behaviour of this function, see the code for details if ( $argument === EXIT_SUCCESS || $argument === EXIT_SPECIAL_SUCCESS || $argument === EXIT_OPTIONS_LISTED || $argument === EXIT_HELP ) { // shortcircuit the "success" cases which don't need an error message exit( $argument ); } $is_debug = defined( 'DEBUG' ) && DEBUG; if ( $print_hint === null ) { $print_hint = $is_debug; } static $map = [ EXIT_SUCCESS => [ 'code' => 0, 'name' => "EXIT_SUCCESS", 'category' => "success", 'description' => "success!", ], EXIT_CANNOT_CONTINUE => [ 'code' => 10, 'name' => "EXIT_CANNOT_CONTINUE", 'category' => "logic exit", 'description' => "cannot continue; but nothing is abnormal or wrong", 'hint' => "consider EXIT_SPECIAL_SUCCESS", ], EXIT_BAD_DATA => [ 'code' => 20, 'name' => "EXIT_BAD_DATA", 'category' => "input error", 'description' => "there was a problem with inputs", ], EXIT_BAD_FORMAT => [ 'code' => 21, 'name' => "EXIT_BAD_FORMAT", 'category' => "input error", 'description' => "data was in an invalid format", ], EXIT_BAD_VALUE => [ 'code' => 22, 'name' => "EXIT_BAD_VALUE", 'category' => "input error", 'description' => "data nominated an invalid value (but was in correct format)", ], EXIT_BAD_COMMAND => [ 'code' => 30, 'name' => "EXIT_BAD_COMMAND", 'category' => "user error", 'description' => "invalid command-line or options", ], EXIT_NO_FILE => [ 'code' => 31, 'name' => "EXIT_NO_FILE", 'category' => "user error", 'description' => "a required file/directory path not nominated", ], EXIT_WRONG_FILE => [ 'code' => 32, 'name' => "EXIT_WRONG_FILE", 'category' => "user error", 'description' => "user nominated file (or directory) missing; use EXIT_FILE_MISSING for system files", ], EXIT_BAD_FILE => [ 'code' => 33, 'name' => "EXIT_BAD_FILE", 'category' => "user error", 'description' => "user nominated file (or directory) cannot be accessed due to invalid permissions", 'hint' => "use EXIT_NO_ACCESS for system files", ], EXIT_USER_CANCEL => [ 'code' => 34, 'name' => "EXIT_USER_CANCEL", 'category' => "user error", 'description' => "user canceled", ], EXIT_BAD_ENVIRONMENT => [ 'code' => 40, 'name' => "EXIT_BAD_ENVIRONMENT", 'category' => "environment error", 'description' => "invalid run-time environment; cannot run", ], EXIT_FILE_MISSING => [ 'code' => 41, 'name' => "EXIT_FILE_MISSING", 'category' => "environment error", 'description' => "a file (or directory) that is expected to always be available is not available", 'hint' => "use EXIT_WRONG_FILE for user nominated files", ], EXIT_NO_ACCESS => [ 'code' => 42, 'name' => "EXIT_NO_ACCESS", 'category' => "environment error", 'description' => "a file (or directory) that should be accessible cannot be accessed due to invalid permissions", 'hint' => "use EXIT_BAD_FILE for user nominated files", ], EXIT_BAD_CONFIG => [ 'code' => 43, 'name' => "EXIT_BAD_CONFIG", 'category' => "environment error", 'description' => "invalid configuration; config file missing or invalid", ], EXIT_NO_LOCK => [ 'code' => 44, 'name' => "EXIT_NO_LOCK", 'category' => "environment error", 'description' => "could not acquire lock", ], EXIT_NO_SERVICE => [ 'code' => 56, 'name' => "EXIT_NO_SERVICE", 'category' => "environment error", 'description' => "cannot establish connection to a required service", 'hint' => "prefer EXIT_NO_DATABASE for database services", ], EXIT_NO_DATABASE => [ 'code' => 57, 'name' => "EXIT_NO_DATABASE", 'category' => "environment error", 'description' => "cannot establish connection to a required database", ], EXIT_EXHAUSTED => [ 'code' => 58, 'name' => "EXIT_EXHAUSTED", 'category' => "environment error", 'description' => "resources exhausted; out of memory, disk space, inodes, etc", ], EXIT_OFFLINE => [ 'code' => 59, 'name' => "EXIT_OFFLINE", 'category' => "environment error", 'description' => "system offline; as configured by administrator", ], EXIT_BAD_PROGRAM => [ 'code' => 70, 'name' => "EXIT_BAD_PROGRAM", 'category' => "program error", 'description' => "an unhandled and fatal situation encountered", ], EXIT_ERROR => [ 'code' => 71, 'name' => "EXIT_ERROR", 'category' => "program error", 'description' => "an error caused process termination", ], EXIT_EXCEPTION => [ 'code' => 72, 'name' => "EXIT_EXCEPTION", 'category' => "program error", 'description' => "an unhandled exception caused process termination", ], EXIT_ASSERT => [ 'code' => 73, 'name' => "EXIT_ASSERT", 'category' => "program error", 'description' => "an assertion violation caused process termination", ], EXIT_TEST_FAILED => [ 'code' => 74, 'name' => "EXIT_TEST_FAILED", 'category' => "program error", 'description' => "test failed; unit-test did not succeed", ], EXIT_INVALID => [ 'code' => 75, 'name' => "EXIT_INVALID", 'category' => "program error", 'description' => "invalid error level; the programmer nominated an invalid error level and the host exited with 85 instead", ], EXIT_NOT_IMPLEMENTED => [ 'code' => 80, 'name' => "EXIT_NOT_IMPLEMENTED", 'category' => "program error", 'description' => "functionality not implemented", ], EXIT_NOT_SUPPORTED => [ 'code' => 81, 'name' => "EXIT_NOT_SUPPORTED", 'category' => "program error", 'description' => "situation not supported", ], EXIT_NOT_POSSIBLE => [ 'code' => 82, 'name' => "EXIT_NOT_POSSIBLE", 'category' => "program error", 'description' => "situation is supposed to be impossible", ], EXIT_DEBUG => [ 'code' => 88, 'name' => "EXIT_DEBUG", 'category' => "program error", 'description' => "programmer exited for debugging purposes", ], EXIT_ABORT => [ 'code' => 89, 'name' => "EXIT_ABORT", 'category' => "program error", 'description' => "programmer aborted with error message", ], EXIT_SPECIAL_SUCCESS => [ 'code' => 90, 'name' => "EXIT_SPECIAL_SUCCESS", 'category' => "special purpose", 'description' => "special operation successful; used for safety (in case improperly invoked)", ], EXIT_OPTIONS_LISTED => [ 'code' => 98, 'name' => "EXIT_OPTIONS_LISTED", 'category' => "special purpose", 'description' => "program options listed; use when programs can be invoked to list their options in a machine readable format", ], EXIT_HELP => [ 'code' => 99, 'name' => "EXIT_HELP", 'category' => "special purpose", 'description' => "help or version number requested ", ], EXIT_UNIX_BAD_PERMISSION => [ 'code' => 126, 'name' => "EXIT_UNIX_BAD_PERMISSION", 'category' => "UNIX error", 'description' => "command was found but could not be executed due to permissions", ], EXIT_UNIX_BAD_COMMAND => [ 'code' => 127, 'name' => "EXIT_UNIX_BAD_COMMAND", 'category' => "UNIX error", 'description' => "command not found or could not be executed", ], EXIT_TERMINATED_BY_SIGNAL => [ 'code' => 128, 'name' => "EXIT_TERMINATED_BY_SIGNAL", 'category' => "UNIX error", 'description' => "process terminated by signal", ], EXIT_TERMINATED_BY_CTRLC => [ 'code' => 130, 'name' => "EXIT_TERMINATED_BY_CTRLC", 'category' => "UNIX error", 'description' => "process terminated by Ctrl+C", ], EXIT_GENERIC_1 => [ 'code' => 1, 'name' => "EXIT_GENERIC_1", 'category' => "general error", 'description' => "error during processing", ], EXIT_GENERIC_2 => [ 'code' => 2, 'name' => "EXIT_GENERIC_2", 'category' => "general error", 'description' => "error during processing", ], EXIT_GENERIC_255 => [ 'code' => 255, 'name' => "EXIT_GENERIC_255", 'category' => "general error", 'description' => "error during processing", ], ]; if ( is_int( $argument ) ) { $spec = $map[ $argument ] ?? $map[ EXIT_INVALID ]; } elseif ( is_string( $argument ) ) { error_log( $argument ); $spec = $map[ EXIT_ABORT ]; } elseif ( $argument instanceof ErrorException ) { $spec = $map[ EXIT_ERROR ]; } elseif ( $argument instanceof AssertionError ) { $spec = $map[ EXIT_ASSERT ]; } elseif ( $argument instanceof Throwable ) { $spec = $map[ EXIT_EXCEPTION ]; } else { $spec = $map[ EXIT_INVALID ]; } $code = $spec[ 'code' ]; if ( $print_error ) { $name = $spec[ 'name' ]; $category = $spec[ 'category' ]; $description = $spec[ 'description' ]; $hint = $spec[ 'hint' ] ?? null; if ( $hint && $print_hint ) { error_log( "hint: $hint" ); } if ( $is_debug ) { error_log( "$category: $description ($name)" ); } else { error_log( "$category: $description" ); } } exit( $code ); }