Error Messages


Posted by Pierre-Edouard Guerin ¡ 4 min read ¡ Published on November 28, 2024

I am an anxious person. So error messages always makes my heart beat faster. Hopefully, following the Pareto Principle, 80% of error messages are mild while 20% are the really tough one. The point is to solve the first kind as quickly as possible and effortless. To do so, allow the user to solve the issue by himself with clear messages and hints (in the case of errors related to input files or parameters). Clear presentation of the context and precise localization of the error in the code will save a lot of useless and tedious work to the developer. The time spared on the easy errors just by having better messages, then can be reallocated to the second kind of errors, the troublemakers.

An unknown error has occurred.

The Ultimate Useless Error Message Ever!

After spending a lot of time redesigning my R package error messages, I have developed good practices for writing helpful console error messages in a standardized way using CLI libraries.

Writing Error Messages

Problem Statement

Error Location

It prevents you to search for hours where the bug occurs in your code.

Error Hint

Example:

❌ Your name Claude63 must contain only letters.

â„šī¸ Check your name does not include numbers.

Punctuation

Where to Write Error Messages in Code

For simple program

For a simple script, the simplest is just to write the error message directly where it happens.

inversion <- function(number) {
  if (number != 0) {
    return(1 / number)
  } else {
    cat("ERROR: Number must be different than zero.")
    cat("INFO: Check that the value of 'number' is different from zero.")
    stop()
  }  
}

For complex program

In complex program such as an R package (I keep this example but the same logic applies for C, java, python, etc.) writing error messages directly in the code is not good.

The solution is to make a dictionnary of error messages. In a dedicated file, for instance error_messages.R, write the complete list of error messages and their associated keys. Then in your code, call the needed error message by its key.

R/
├── error_messages.R
└── main.R

error_messages.R

error_messages$err001 = c(
    "ERROR: Number must be different than zero.",
    "INFO: Check that the value of 'number' is different from zero."    
    )
error_messages$err002 = ...

main.R

inversion <- function(number) {
  if (number != 0) {
    return(1 / number)
  } else {
    error_messages$err001
  }  
}

This way you code is shorter, cleaner and you can change all the error messages in a centralized way.

Command Line Interfaces

Last but not least, you need to use a dedicated Command Line Interfaces (CLI) for error messages. Still with the example of R, the package cli provide the tools to build structured messages.

The following command display a default format for Success, alert, warning, hint type messages. It also automatically generate the stack track to localize the where the error occurs.

cli_alert_success("Success.")
cli_alert_danger("Alert.")
cli_alert_warning("Warning.")
cli_alert_info("Hint.")

Text formatting

cli_h1("Heading 1")
cli_h2("Heading 2")
cli_li("Item 1")
cli_li("Item 2")

Using variable

fruits <- c("banana", "orange", "strawberry")
cli_alert_success("Recorded {length(user_names)} fruits.")

Pluralize words

n_files = 3
n_dirs = 1
cli_alert_info("Found {n_files} file{?s} and {n_dirs} director{?y/ies}.")

Dictionnary of error messages combined with cli

R/
├── error_messages.R
└── main.R

error_messages.R

error_messages$err001 =
  c(
    "x" = "{.field number} must be different from zero.",
    "i" = "The value of {.field number} is {.val {number}}.",
    ">" = "Check {.field number} is not zero."
  )

error_messages$err002 = ...

main.R

inversion <- function(number) {
  if (number != 0) {
    return(1 / number)
  } else {
    cli_abort(error_messages$err001)
  }  
}

CLI Libraries in Other Languages

Conclusion

References







Relevant Tags

About the Author