30

What is best practice when a unhandled exceptions occurs in a desktop application?

I was thinking about to show a message to the user, so that he can contact support. I would recommend to the user to restart the application, but not force it. Similar to what is discussed here: ux.stackexchange.com - What's the best way to handle unexpected application errors?

The project is a .NET WPF application, so the described proposal could look like this (Note that this is a simplified example. Probably it would make sense to hide the exception details until the user click on "Show Details" and provide some functionality to easily report the error):

public partial class App : Application
{
    public App()
    {
        DispatcherUnhandledException += OnDispatcherUnhandledException;
    }

    private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        LogError(e.Exception);
        MessageBoxResult result = MessageBox.Show(
             $"Please help us fix it and contact support@example.com. Exception details: {e.Exception}" +
                        "We recommend to restart the application. " +
                        "Do you want to stop the application now? (Warning: Unsaved data gets lost).", 
            "Unexpected error occured.", MessageBoxButton.YesNo);

        // Setting 'Handled' to 'true' will prevent the application from terminating.
        e.Handled = result == MessageBoxResult.No;
    }

    private void LogError(Exception ex)
    {
        // Log to a log file...
    }
}

In the implementation (Commands of ViewModels or event handler of external events) I would then only catch the specific exogenous exception and let all other exceptions (boneheaded and unknown exceptions) bubble up to the "Last resort handler" described above. For a definition of boneheaded and exogenous exceptions have a look at: Eric Lippert - Vexing exceptions

Does it make sense to let the user decide if the application should be terminated? When the application is terminated, then you for sure have no inconsistent state... On the other hand the user may loose unsaved data or is not able to stop any started external process anymore until the application is restarted.

Or is the decision if you should terminate the application on unhandled exceptions depending of the type of application you are writting? Is it just a trade off between "robustness" vs. "correctness" like described in Code Complete, Second Edition

To give you some context what kind of application we are talking about: The application is mainly used to control chemical lab instruments and show the measured results to the user. To do so the WPF applications communicates with some services (local and remote services). The WPF application does not communicate directly with the instruments.

  • 27
    If you didn't expect an exception, how can you be sure the application can safely keep plodding on? – Deduplicator Oct 7 at 15:33
  • 2
    @Deduplicator: Of course, you can't be sure. Like already written as comment to Matthew's answer: "Yes, of course the application could be in an invalid state. Maybe some ViewModel's only got updated partly. But can this do any harm? The user can reload the data and if something invalid will be send to the service, then the service will not accept it anyway. Isn't it better for the user if he is able to save before he restarts the application?" – Jonas Benz Oct 7 at 16:03
  • 2
    @Voo So, you make sure the application can safely keep going by always expecting an exception? Seems you are denying the premise of receiving an unexpected exception. – Deduplicator Oct 8 at 12:42
  • 2
    In any case, please make the error message copyable. Alternatively, say in which log file it's been written. – ComFreek Oct 8 at 13:49
  • 2
    Handling doesn't necessarily imply explicit action. If you can be sure the application can continue safely, you have handled the exception. – chepner Oct 8 at 20:55
47

You have to expect your program to terminate for more reasons than just an unhandled exception anyway, like a power failure, or a different background process which crashes the whole system. Therefore I would recommend to terminate and restart the application, but with some measures to mitigate the consequences of such a restart and minimize the possible data loss.

Start with analysing the following points:

  • How much data can actually get lost in case of a program termination?

  • How severe is such a loss really for the user? Can the lost data reconstructed in less than 5 minutes, or are we talking about losing a days work?

  • How much effort is it to implement some "intermediate backup" strategy? Don't rule this out because "the user would have to enter a change reason" on a regular save operation, as you wrote in a comment. Better think of something like a temporary file or state, which may be reloaded after a program crash automatically. Many types of productivity software does this (for example MS Office and LibreOffice both have an "autosave" feature and crash recovery).

  • In case data was wrong or corrupted, can the user see this easily (maybe after a restart of the program)? If yes, you may offer an option to let the user save the data (with some small chance it is corrupted), then force a restart, reload it and let the user check if the data looks fine. Make sure not to overwrite the last version that was saved regularly (instead write to a temporary location/file) to avoid corrupting the old version.

If such an "intermediate backup" strategy is a sensible option depends ultimately on the application and its architecture, and on the nature and structure of the data involved. But if the user will loose less than 10 minutes of work, and such a crash happens once a week or even more seldom, I would probably not invest too much thought into this.

  • 10
    en.wikipedia.org/wiki/Crash-only_software, and this is how Android apps work by necessity. – Mooing Duck Oct 8 at 1:19
  • 3
    Excellent answer - and a nice example on considering things in a broader context (in this case "how can we prevent data loss in any case of crash?") leads to a better solution. – sleske Oct 8 at 9:26
  • 1
    I made a small edit to note that you should not overwrite old data - hope you don't mind. – sleske Oct 8 at 9:26
  • 1
    @MooingDuck Plenty of Android apps (such as games) lose their state on a crash. – user253751 Oct 8 at 9:52
  • 1
    @immibis: Yes, Android has a large number of really low quality apps indeed. – Mooing Duck Oct 8 at 21:55
30

It depends to some extent on the application you're developing but in general, I'd say that if your application encounters an unhandled exception, you need to terminate it.

Why?

Because you can no longer have any confidence in the state of the application.

Definitely, provide a helpful message to the user, but you should ultimately terminate the application.

Given your context, I would definitely want the application to terminate. You do not want software running in a lab to produce corrupt output and since you didn't think to handle the exception, you have no idea why it was thrown and what is happening.

  • I tried to add some context information about the application in the last part. – Jonas Benz Oct 7 at 15:50
  • 10
    @JonasBenz Isn't it better for the user if he is able to save before he restarts the application? Yes, but how do you know if the data the user would be saving is valid and not corrupt? At this point, you've got an unexpected exception and you really don't know why. Your safest path, albeit annoying to the user, is to terminate the application. If you're concerned about the user saving work, you would employ a constant-saving strategy. Again, it all depends on the application you're writing. – Matthew Oct 7 at 16:11
  • 4
    Yes, I can argue the same way here: I disagree with the presence of a Continue button. The problem is simply that if you, the application developer don't know if you can continue safely, how can the user know? If you get an unhandled exception, it means you have an error you didn't expect and you can't say for sure what's happening at this point. The user will want to continue because they won't want to lose their work, I understand that, but do you want to let them continue even if your application could be producing bad results because of this error? – Matthew Oct 7 at 17:32
  • 3
    @Matthew "if you, the application developer don't know if you can continue safely, how can the user know", the developer didn't know when they wrote the code. When user encounters a specific bug like this, it may be known. And user may find out from whatever user forums, support channels and whatnot, or simply by testing and seeing what happens to their data... I agree it's still a bit too obscure and dangerous as user feature, just pointing out that time makes it possible for the user to indeed know if "continue" is sensible or not. – hyde Oct 8 at 10:53
  • 1
    @JonasBenz, under Windows 3.1, the dialog box that appeared when a program performed an illegal memory access had an "ignore" button that let the program continue running. You'll note that every subsequent version of Windows does not have that button. – Mark Oct 9 at 22:20
12

Considering that this is meant for a chemical lab and that your application does not control the instruments directly but rather through other services:

Force termination after showing the message. After an unhandled exception your application is in an unknown state. It could send erroneous commands. It can even invoke nasal demons. An erroneous command could potentially waste expensive reagents or bring danger to equipment or people.

But you can do something else: gracefully recover after restarting. I assume that your application doesn't bring down those background services with itself when it crashes. In that case you can easily recover the state from them. Or, if you have more state, consider saving it. In a storage which has provisions for data atomicity and integrity (SQLite maybe?).

Edit:

As stated in the comments, the process you control may require changes fast enough that the user won't have time to react. In that case you should consider silently restarting the app in addition to graceful state recovery.

  • Termination in a state that requires follow up commands RIGHT NOW could be just as dangerous in chemical lab. – Oleg V. Volkov Oct 10 at 1:53
  • @OlegV.Volkov so maybe restart yourself upon termination? On a decent computer starting a GUI should take on the order of hundreds of milliseconds top. If the process requires harder timings control wouldn't be implemented on a non-realtime OS. Although it's OP who should do the final risk assessment. – Jan Dorniak Oct 10 at 8:01
  • @OlegV.Volkov that's a good point though, so I added my opinion on it in the answer. – Jan Dorniak Oct 10 at 8:04
8

Trying to generally answer this question at the top level of the program is not a smart play.

If something has bubbled up all the way, and at no point in the architecture of the application did anyone consider this case, you have no generalizations you can make about what actions are, or are not, safe to take.

So, no, it is definitely not a generally acceptable design to allow the user to choose whether or not the application attempts to recover, because the application and the developers demonstratively have not done the due diligence necessary to find out if that's possible or even wise.

However, if the application has high-value portions of it's logic or behavior that have been engineered with this sort of failure recovery in mind, and it is possible to leverage them in this case, then by all means, do so - In that case, it may be acceptable to prompt the user to see if they want to attempt recovery, or if they would like to just call it quits and start over.

This sort of recovery is not generally necessary or advisable for all (or even most) programs, but, if you are working on a program for which this degree of operational integrity is required, that might be a circumstance in which presenting this sort of a prompt to a user would be a sane thing to do.

In leiu of any special failure recovery logic - No, don't do this. You literally have no idea what will happen, if you did, you'd have caught the exception further down and handled it.

  • Unfortunately, many methods for things like "Build an object with data received from some location" make no distinction between exceptions that indicate that the action could not be completed, but the attempt had no side-effects, versus those that indicate that something more serious has gone wrong. The fact that an attempt to load a resource failed for some reason one hadn't anticipated shouldn't force a fatal error if one is generally prepared for the inability to construct the object. What matters is side-effects, which unfortunately is something exception frameworks ignore. – supercat Oct 9 at 23:01
  • @supercat - If you can identify an error, you can handle it. If you can't identify it, you can't handle it, unless you write a routine to run an integrity check on the state of the application to try to sleuth out what might have gone wrong. It doesn't matter what the error 'might have been,' we've expressly established that we don't know that by the fact that we're trying to generally handle uncaught exceptions. – Iron Gremlin Oct 10 at 21:09
3

The problem with "exceptional exceptions", i.e. exceptions that you haven't foreseen, is that you don't know which state the program is in. For example, trying to save the user's data could actually destroy even more data.

For that reason, you should terminate the application.

There is a very interesting idea called Crash-only Software by George Candea and Armando Fox. The idea is that if you design your software in such a way that the only way to close it is to crash it and the only way to start it is to recover from a crash, then your software will be more resilient, and the error recovery code paths will be much more thoroughly tested and exercised.

They came up with this idea after noticing that some systems started faster after a crash than after an orderly shutdown.

A good, although no longer relevant example, are some older versions of Firefox that not only start faster when recovering from a crash, but also have a better startup experience that way! In those versions, if you shut down Firefox normally, it would close all open tabs and start up with a single empty tab. Whereas when recovering from a crash, it would restore the open tabs at the time of the crash. (And that was the only way to close Firefox without losing your current browsing context.) So, what did people do? They simply never closed Firefox and instead always pkill -KILL firefoxed it.

There is a nice writeup about crash-only software by Valerie Aurora on Linux Weekly News. The comments are also worth a read. For example, someone in the comments rightfully points out that those ideas are not new, and are in fact more or less equivalent to the design principles of Erlang/OTP based applications. And, of course, looking at this today, another 10 years after Valerie's and 15 years after the original article, we might notice that the current micro service hype is re-inventing those same ideas yet again. Modern Cloud-scale data center design is also an example on a coarser granularity. (Any computer can crash at any time without affecting the system.)

It is, however, not enough to just let your software crash. It has to be designed for it. Ideally, your software would be broken up into small, independent components that each can crash independently. Also, the "crash mechanism" should be outside of the component that is being crashed.

1

The proper way to handle most exceptions should be to invalidate any object that might be in a corrupt state as a consequence, and continue execution if invalidated objects don't prevent that. For example, the safe paradigm for updating a resource would be:

acquire lock
try
  update guarded resource
if exception
  invalidate lock
else
  release lock
end try

If an unexpected exception occurs while updating the guarded resource, the resource should be presumed in a corrupt state, and the lock invalidated, regardless of whether the exception is of a type that would otherwise be benign.

Unfortunately, resource guards implemented via IDisposable/using will get released whenever the guarded block exits, without any way of knowing whether the block exited normally or abnormally. Thus, even though there should be well-defined criteria for when to continue after an exception, there's no way of telling when they apply.

  • +1 simply for expressing this relatively non-obvious and still non-common perspective on what the proper way is. I don't actually know yet if I agree with it, because this is a novel heuristic/rule to me, so I have to mull it over for a while, but it seems plausibly-wise. – mtraceur Oct 9 at 20:43
0

You might use the approach that every single iOS and MacOS app follows: An uncaught exception takes down the application immediately. Plus many errors, like array out of bounds or just arithmetic overflow in newer applications do the same. No warning.

In my experience many users don’t take any notice but just tap in the app icon again.

Obviously you need to make sure that such a crash doesn’t lead to significant data loss and definitely doesn’t lead to costly mistakes. But an alert “Your app will crash now. Call support if it bothers you” isn’t helping anyone.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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