3

There are cases where I don't know how to use exception handling. To make it clear, let me divide exceptions into two types:

  1. exceptional cases that may happen occasionally, such as when you try to open a non-existent file.

  2. exceptions that you wouldn't expect to happen if you have written your program correctly, such as out-of-range indexing.

In the first case I'd prompt the user and/or return the program to a normal state, but I don't know what to do for the second type. If I don't catch them, the runtime will show its own message. If I wanted to handle it myself, I would have no idea of how to handle a case that was not supposed to happen whatsoever.

4
  • 2
    Possible duplicate of How should I handle exception that *should* never be thrown?
    – gnat
    Jan 15, 2017 at 10:25
  • 1
    Depends a lot on the application. Some applications can abort the whole program (typical short-lived command line tools), a webserver with little shared state between requests and a transactional database can just return a 500 from that request. Some other programs have a big program because now they have corrupted internal state. Jan 15, 2017 at 11:21
  • The general advice is to prevent that from happening, because, in general, prevention is better than the cure. Recovery from errors can cost performance (time and space), difficulty (implementation effort), and is sometimes outright impossible. Once you have accumulated enough experience to reduce unexpected bugs from incorrect code, your question (i.e. the subject of this question) will shift in other directions. It is because of this reason that your question means different things to different programmers.
    – rwong
    Jan 15, 2017 at 12:38
  • 1
    @gnat: that question isn't this one, and is marked as aduplicate of yet another question, which also isn't this one (or the one that it is marked as dupicate of). They are somewhat similar, but are distinct.
    – jmoreno
    Jan 15, 2017 at 12:50

6 Answers 6

9

For type 2 exceptions, you should terminate the program. You don't have to let the runtime show its own error, you can catch the error at the top level and show your own message (and log the origial exception somewhere). But you should not allow the program to continue.

Since the program is in a state which "should not happen", you have no idea about what cause the issue and how much of the program state which is corrupted. There is no way to gracefully recover from such an issue, and ignoring it will just cause more problem down the line since you are already processing corrupt data.

There are a few cases where it is possible to recover from a type 2 error. If it happens in a isolated subsystem, where you are able to discard all output from the subsystem and it is not critical for the continuation of the program. Say you have a spell check component in an editor. A type 2 error can be recovered from by simply disabling this component, or maybe restarting the component. But this is only possible with sub-systems which are carefully designed to be isolated and non-critical.

1
  • Absolutely! This is almost a cardinal rule for programming --- It's like driving a car with no brake pads, you can for a little bit, but eventually you are going to do MAJOR damage to your car, but if your stereo does not work...oh well, it is not mission critical. For rule 1, an option is to ask for user input on those kinds of things (i.e. put a window up asking for a new directory for the file because the "current" or "default" one does not exist)
    – G.T.D.
    Jan 15, 2017 at 17:06
3

One thing, when a programming error is detected (for example because an exception is thrown) then during development, the developer must be informed, so that the bug in the code is fixed. So make sure that during development, you will be notified when such a bug happens.

If this happens in the hands of a customer, there are some considerations. An important one: Do NOT EVER allow a situation where the user is stuck. So if you read a file as part of normal operation, and this fails and your app crashes, and you start the app again and it reads the same file and it fails again and you start the app again and so on, then the user has a serious problem. Make sure that kind of thing doesn't happen - if reading this file fails, and you know it's going to fail again if you try again, then you might delete that file.

Other than that, you have two choices usually: Cancel the user operation that caused the problem, or exit the program. If the operation has made partial permanent changes, you have a problem. If you suspect that the bug could have caused damage, do what you need to do so that the bug doesn't become permanent. For example if a customer ordered five items, and creating a bill has an exception at item #2, you don't want to send out a bill for four items only, you'd rather cancel the whole operation. If you suspect that unnoticed damage elsewhere has caused your bug, you may consider quitting the application.

There is no simple, general rule.

2

exceptions that you wouldn't expect to happen if you have written your program correctly

It really depends on your application.

If particular, if it's a "critical" application, you could conceivably isolate all major units of work into transactions. If an exception occurs within a transaction, you have retry and recovery/rollback for options. What you do specifically depends on the purpose of your application.

For less critical stuff, catch all exceptions globally: give the user a friendly message and offer to report the exception before quitting the program.

2

Main/Short Answer

Depends on your program, the needs of said program, and the critical level of said program.

Slightly Longer Answer

A good "shotgun-catch-all" solution is to see if your program's coding language has some kind of default error handling method or class that ALL errors pass through and just override it to catch the error, log it/report it to the developer, and then kill the program.

Long Answer

If you develop your program in some fashions (i.e. isolated and encapsulated modules) you can, theoretically (because it is dangerous to keep going if any error is found), reset that module and keep going/retry on that module.

The biggest drawback to this is that you are hoping the problem section (whatever that may be) will fix itself after umpteen iterations of it. It is almost like accepting insanity as a solution (keep trying expecting a different result)...but for some programs, that MUST be done because they are so mission critical (think of programs used in medical situations...they CANNOT fail and must keep going no matter what).

Keep in mind, the "accept insanity" solution can also present the user with a never ending program (i.e. they cannot proceed or the computer gets stuck in a loop trying to fix the program).

It is a double edged sword, yes you can design the program to keep going and reset the problem section and that has the potential of introducing errors later on because of corrupt data and making the end user "repeat" the process until fixed.

On the other hand, killing the program for every error can make your program seem unstable or poorly designed even if it is the user's fault for making the program crash (i.e. the art of user input validation) or can cause the developer to start getting lazy with bug fixes (because they get bombarded) by using things like try-catch blocks everywhere (I saw this on a project one time...almost every method was try-catch'ed).

Like my first line, it really depends on your program.

0

Some languages have an additional mechanism of assertions. While exceptions indicate exceptional cases, such as a network failure during a transfer, or a failure to read a file because of the wrong permissions being set, failed assertions indicate the bugs in the application itself.

While exceptions could be handled, for instance retry the transfer on network failure or ask the user to fix the permissions on a file, or unhandled, leading to a global exception handler to be activated, assertion failures are always unhandled, that is they are handled only globally.

As a result, when such assertion fails:

  • Either you let the application crash, and rely on the operating system to let you know about the issue,

  • Or you log the issue and let the application crash,

  • Or you log the issue and show an apologizing message to the user.

-2

The source of the error doesn't matter. Exception handling is all about recovery -- what can you do to continue with as little damage and inconvience to the user as possible.

Broadly speaking, recovery generally takes one of two forms, aborting the action or trying an alterative action that achieves the same goal.

Error logging should done in either case.

7
  • 5
    When handling an expected exception (in languages where that's a thing) you understand the cause and thus you can design an appropriate reaction. When the exception is caused by a bug, you by definition don't understand what caused it, so the response will generally be different. For some exceptions (e.g. access violation) there isn't really anything you can do apart from aborting the process, since it's typically caused by irrecoverable data corruption. Jan 15, 2017 at 11:48
  • A few things to add. "continue", "preventing or limiting damage", and "as little inconvenience to the user" are actually quite different goals; sometimes they are conflicting goals and a tradeoff must be made. @CodesInChaos's comment is about certain lower-level languages where, by the time an unexpected (impossible) error happens, it is the result of an memory corruption by a different culprit (i.e. it causes a crash at an innocent code location), that the only sane option is to save a crash dump immediately and terminate. This doesn't need to happen in higher languages.
    – rwong
    Jan 15, 2017 at 11:56
  • Depending on the worst possible scenario, some recovery can be done within the same process; some can only be done after a program restart. If a process doesn't expose invalid data e.g. in file system or database, it could be terminated and restarted as if the process had not been executed before. If any external state is modified, it must be restored somehow. Techniques are needed to make external data recovery possible. For example, one can make a copy of external data before modification. One can write new data to a staging location, and only overwrite (commit) upon success. Etc.
    – rwong
    Jan 15, 2017 at 12:01
  • 1
    @rwong If the application holds complex state in memory (e.g. a game or a program that opens-edits-saves a document like MS Word) then you can get corruption even high level languages, even if it is less severe than memory corruption. if you're lucky, it might be feasible to do a consistency check to see if the state is fine. In my experience the hardest trade-off is between preserving work/state and limiting the spread of corruption. Jan 15, 2017 at 12:12
  • 2
    @jmoreno Both of those are of the expected kind. But if you get an InvalidOperationException in C# or access an array index out of bounds, all you know is that something is horribly wrong. But you don't know if this is caused by the application state being not like it is supposed to be, or if your current operation is buggy. Unless you have snapshot of the state before the current operation (typically thanks to a transactional DB) there isn't much you can do. Jan 15, 2017 at 12:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.