Error levels

From John's wiki
Revision as of 07:10, 18 February 2024 by Sixsigma (talk | contribs) (→‎BASH code)
Jump to navigation Jump to search

TODO: not_implemented, not_supported, not_possible

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:

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)
40 user error EXIT_BAD_COMMAND 400 invalid command-line or options
41 user error EXIT_NO_FILE 400 a required file/directory path was not nominated
42 user error EXIT_WRONG_FILE 400 user nominated file (or directory) is missing use EXIT_FILE_MISSING for system files
43 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
60 environment error EXIT_BAD_ENVIRONMENT 503 invalid run-time environment; cannot run
61 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
62 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
63 environment error EXIT_BAD_CONFIG 503 config file missing or invalid
76 environment error EXIT_NO_SERVICE 503 cannot establish connection to a required service prefer EXIT_NO_DATABASE for database services
77 environment error EXIT_NO_DATABASE 503 cannot establish connection to a required database
78 environment error EXIT_EXHAUSTED 507 resources exhausted; out of memory, disk space, inodes, etc
79 environment error EXIT_OFFLINE 503 system offline; as configured by administrator
80 program error EXIT_BAD_PROGRAM 500 an unhandled and fatal situation encountered
81 program error EXIT_ERROR 500 an error caused process termination
82 program error EXIT_EXCEPTION 500 an unhandled exception caused process termination
83 program error EXIT_ASSERT 500 an assertion violation caused process termination
84 program error EXIT_TEST_FAILED 500 test failed; unit-test did not succeed
85 program error EXIT_INVALID 500 invalid error level; the programmer nominated an invalid error level and the host exited with 85 instead
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-39 input error invalid data
40-59 user error invalid command-line or options
60-79 environment error improper run-time environment
80-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
EXIT_SUCCESS=0                 # success!
EXIT_CANNOT_CONTINUE=10        # logic exit: cannot continue; but nothing is abnormal or wrong: consider EXIT_SPECIAL_SUCCESS
EXIT_BAD_DATA=20               # input error: there was a problem with inputs
EXIT_BAD_FORMAT=21             # input error: data was in an invalid format
EXIT_BAD_VALUE=22              # input error: data nominated an invalid value (but was in correct format)
EXIT_BAD_COMMAND=30            # user error: invalid command-line or options
EXIT_NO_FILE=31                # user error: a required file/directory path not nominated
EXIT_WRONG_FILE=32             # user error: user nominated file (or directory) missing; use EXIT_FILE_MISSING for system files
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
EXIT_BAD_ENVIRONMENT=40        # environment error: invalid run-time environment; cannot run
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
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
EXIT_BAD_CONFIG=43             # environment error: invalid configuration; config file missing or invalid
EXIT_NO_SERVICE=56             # environment error: cannot establish connection to a required service: prefer EXIT_NO_DATABASE for database services
EXIT_NO_DATABASE=57            # environment error: cannot establish connection to a required database
EXIT_EXHAUSTED=58              # environment error: resources exhausted; out of memory, disk space, inodes, etc
EXIT_OFFLINE=59                # environment error: system offline; as configured by administrator
EXIT_BAD_PROGRAM=70            # program error: an unhandled and fatal situation encountered
EXIT_ERROR=71                  # program error: an error caused process termination
EXIT_EXCEPTION=72              # program error: an unhandled exception caused process termination
EXIT_ASSERT=73                 # program error: an assertion violation caused process termination
EXIT_TEST_FAILED=74            # program error: test failed; unit-test did not succeed
EXIT_INVALID=75                # program error: invalid error level; the programmer nominated an invalid error level and the host exited with 85 instead
EXIT_NOT_IMPLEMENTED=80        # program error: functionality not implemented
EXIT_NOT_SUPPORTED=81          # program error: functionality not supported
EXIT_NOT_POSSIBLE=82           # program error: situation is supposed to be impossible
EXIT_DEBUG=88                  # program error: programmer exited for debugging purposes
EXIT_ABORT=89                  # program error: programmer aborted with error message
EXIT_SPECIAL_SUCCESS=90        # special purpose: special operation successful; used for safety (in case improperly invoked)
EXIT_OPTIONS_LISTED=98         # special purpose: program options listed; use when programs can be invoked to list their options in a machine readable format
EXIT_HELP=99                   # special purpose: help or version number requested 
EXIT_UNIX_BAD_PERMISSION=126   # UNIX error: command was found but could not be executed due to permissions
EXIT_UNIX_BAD_COMMAND=127      # UNIX error: command not found or could not be executed
EXIT_TERMINATED_BY_SIGNAL=128  # UNIX error: process terminated by signal
EXIT_TERMINATED_BY_CTRLC=130   # UNIX error: process terminated by Ctrl+C
EXIT_GENERIC_1=1               # general error: error during processing
EXIT_GENERIC_2=2               # general error: error during processing
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( 'EXIT_SUCCESS', 0 );
define( 'EXIT_CANNOT_CONTINUE', 10 );
define( 'EXIT_BAD_DATA', 20 );
define( 'EXIT_BAD_FORMAT', 21 );
define( 'EXIT_BAD_VALUE', 22 );
define( 'EXIT_BAD_COMMAND', 30 );
define( 'EXIT_NO_FILE', 31 );
define( 'EXIT_WRONG_FILE', 32 );
define( 'EXIT_BAD_FILE', 33 );
define( 'EXIT_BAD_ENVIRONMENT', 40 );
define( 'EXIT_FILE_MISSING', 41 );
define( 'EXIT_NO_ACCESS', 42 );
define( 'EXIT_BAD_CONFIG', 43 );
define( 'EXIT_NO_SERVICE', 56 );
define( 'EXIT_NO_DATABASE', 57 );
define( 'EXIT_EXHAUSTED', 58 );
define( 'EXIT_OFFLINE', 59 );
define( 'EXIT_BAD_PROGRAM', 70 );
define( 'EXIT_ERROR', 71 );
define( 'EXIT_EXCEPTION', 72 );
define( 'EXIT_ASSERT', 73 );
define( 'EXIT_TEST_FAILED', 74 );
define( 'EXIT_INVALID', 75 );
define( 'EXIT_NOT_IMPLEMENTED', 80 );
define( 'EXIT_NOT_SUPPORTED', 81 );
define( 'EXIT_NOT_POSSIBLE', 82 );
define( 'EXIT_DEBUG', 88 );
define( 'EXIT_ABORT', 89 );
define( 'EXIT_SPECIAL_SUCCESS', 90 );
define( 'EXIT_OPTIONS_LISTED', 98 );
define( 'EXIT_HELP', 99 );
define( 'EXIT_UNIX_BAD_PERMISSION', 126 );
define( 'EXIT_UNIX_BAD_COMMAND', 127 );
define( 'EXIT_TERMINATED_BY_SIGNAL', 128 );
define( 'EXIT_TERMINATED_BY_CTRLC', 130 );
define( 'EXIT_GENERIC_1', 1 );
define( 'EXIT_GENERIC_2', 2 );
define( '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_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_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'  => "functionality 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 );

}