Error handling (WIP!!)

Last modified on August 27, 2019 by Alex


Error-handling code is in general the least understood, documented and tested part of a software system written in low-level languages like C/C++, and as a consequence, the buggiest.

Error handling bugs:

  • Dropped Unhandled Errors
  • Error Code Mismatches Between Code and Documentation

Encapsulation problem

A typical example:

  • Minor hiccup reporting – Work around the problem automatically, don’t bother telling anybody.
  • Unexpected condition – Throw an exception.
  • Resolvable exception – Resolve it.
  • Propagating exceptions loses contextual information that an administrator needs to find and solve a problem. You need to log that information before throwing the exception. So put that contextual information in the exception, or throw a different exception that has the information you need.
  • Propagating an exception up the call chain of a call-back in an event-driven system passes the error information in the wrong direction, away from the application layers towards generic I/O layers. Lower layers do not know how to handle these errors. The interfaces through which they invoke application functionality might not allow exceptions to be thrown. Throwing unchecked exceptions might put the lower layers into an unspecified state. So in these cases, use a mechanism other than an exception.
  • Consider also use of ResumableException or AbortRetryIgnore pattern. Admittedly, language support for this doesn’t exist outside of Lisp, but the pattern itself allows recovery from exception in accordance with a policy defined in a higher layer. (http://wiki.c2.com/?LoggingDiscussion)
  • Top-level environmental exception, like “database is down” – Send an email to the sysadmin (or whoever is in charge of fixing these things), or page him. He’s not going to read the log file unless he has to, so the software should be proactive.

Some other top-level exception – Must be a bug. Send an email to the programming team, get that sucker squashed. They’re not going to read the log file unless they have to, so the software should be proactive. Ooops, you’ve lost all the contextual information you need by propagating exceptions rather than logging errors. If you can log the contextual information, you can propagate it with the exception.

Before we decide where to add log points, it is useful tounderstand how a failure happens. In his seminal worktwo decades ago, J.C. Laprie decomposed the structuralelements of system failures—fault, error and failure—intoa model that is widely used today [20]. As shown in Fig-ure 3, afaultis a root cause, which can be a software bug, ahardware malfunction, or a misconfiguration. A fault canproduce abnormal behaviors referred to aserrors. How-ever, some of these errors will have no user-perceivableside-effects or may be transparently handled by the sys-tem. It is only the subset of remaining errors which furtherpropagate and become visible to users that are referred toasfailures, such as crash, hang, incorrect result, incom-plete functionality, etc.To further inform our choice of where to place log state-ments, we divide errors into two categories:(i) Detected errors (i.e., exceptions): Some errors arechecked and caught by a program itself. For example, itis a commonly accepted best practice to check library orsystem call return values for possible errors.(ii) Undetected errors: Many errors, such as incorrectvariable values, may be more challenging to detect mech-anistically. Developers may not know in advance whatshould be a normal value for a variable. Therefore, someerrors will always remain latent and undetected until theyeventually produce a failure.

To dive in one step further, detected errors can be han-dled in three different ways:(i) Early termination: a pro-gram can simply exit when encounteringan error.(ii) Cor-rect error handling: a program recovers from an error ap-propriately, and continues execution.(iii) Incorrect errorhandling: a program does not handle the error correctlyand results in an unexpected failure.

These distinctions provide a framework for consideringthe best program points for logging. In particular, detectederrors are naturally “log-worthy” points. Obviously, if aprogram is about to terminate then there is a clear causalrelation between the error and the eventual failure. More-over, even when a program attempts to handle an error, itsexception handlers are frequently buggy themselves sincethey are rarely well tested [30, 17, 16]. Consequently, log-ging is appropriate in most cases where a program detectsan error explicitly—as long as such logging does not in-troduce undue overhead. Moreover, logging such errorshas no runtime overhead in the common (no error) case.

DO NOT use built-in exceptions except for preconditions or assertions

Java provides a wide variety of built-in exceptions. These exceptions are used by the Java core API, but can also be reused by developers. In our study, some participants mentioned that the reuse of built-in exceptions was a guideline that should be adopted in their systems. The reuse of IllegalArgumentException and IllegalStateException was mentioned by participants. The IllegalArgumentException is used to validate the input arguments of a method. The IllegalStateException validates the state of the operation or related objects. The use of NullPointerException was also cited by 1 participant.

In addition to the direct reuse of Java types in signaling, some respondents indicated that such built-in exceptions should be extended, creating specific custom exceptions.

DO NOT have a single top-level exception for every error

Define custom exceptions:The creation of customexceptions was one of the guidelines mentioned by the par-ticipants (P8 P13 P35 P49 P56 P66 P74 P76 P86 P91). Theanswers indicated that custom exceptions could be used indifferent ways in a project. One way is to have a customexception to be a super type of project exceptions (P8 P35P74 P76).“All domain-specific exceptions are a subclass of asingle special abstract exception.”P76A second reason for defining custom exceptions was to usea single custom exception for the entire project (P49 P66).“… all exceptions are encapsulated in a customizedexception…” https://arxiv.org/pdf/1901.08718.pdf

Throw specific exceptions:Exceptions, just likeany class, can be defined at different levels of abstrac-tion. Types such asException,RuntimeException,or a custom type that represents the root of failures ina project are generic exceptions. Specific exceptions arethe opposite. They represent a well-defined fault, such asIllegalArgumentException. Participants (P8 P12 P13P56 P69 P74 P76) reported that the creation and use of specificexceptions were a guideline in their projects.

  1. Use exceptions to represent bad input:Exceptions canrepresent failures of different origins and natures. Participants(P2 P28 P60 P71 P85 P96) reported creating and signalingexceptions to represent bad input. In most cases, the bad inputwas given by the user (P60 P71 P85 P96).

https://users.encs.concordia.ca/~shang/pubs/MSR2018_GUI.pdf

References

  • [1] - http://250bpm.com/blog:140 http://wiki.c2.com/?WhatAreAssertions http://wiki.c2.com/?UseAssertions http://wiki.c2.com/?DoNotUseAssertions http://wiki.c2.com/?ReplaceCommentWithAssertion http://wiki.c2.com/?ShipWithAssertionsOn http://wiki.c2.com/?KeepErrorInformation

https://www.fpcomplete.com/blog/2016/11/exceptions-best-practices-haskell https://www.reddit.com/r/haskell/comments/amvau1/reconciling_logging_with_error_handling/ https://underscore.io/blog/posts/2015/02/23/designing-fail-fast-error-handling.html https://jeffsiver.com/2015/12/30/exception-handling-antipatterns/ https://smsohan.com/blog/2019/05/23/exception-handling-anti-patterns/