28

When designing exceptions should I write messages that a user or a developer should understand? Who should actually be the reader of exception messages?

I find exception messages aren't useful at all and I always have a hard time writing them. By convention the type of the exception should already tell us why something didn't work and custom properties might add even more information like file names, indexes, keys etc. so why repeating it in the message itself? An autogenerated message could also do and all what it had to contain is the name of the exception with a list of additional properties. This would be exactly as useful as a handwritten text.

Wouldn't it be better to not write messages at all but instead have special exception renderers that takes care of creating meaningful messages maybe in different languages rather then hardcoding them in code?


I've been asked whether either of those questions provide an answer to my question:

I've read them both and I wasn't happy with their answers. They talk about users in general and focus on the content of the message itself rather then on the addressee and it turns out there can be at least two of them: the end-user and the developer. I never know which one I should speak to when writing exception messages.

I even think that the famous message doesn't have any real value at all as it just repeats the name of the exception type in different words so why bother writing them at all? I can perfectly generate them automatically.

To me exception message lacks a differentiation of its readers. A perfect exception would need to provide at least two versions of the message: one for the end-user and one for the developer. Calling it just a message is too generic. A developer message should be then written in English but the end-user's message might need to be translated into other languages. It is not possible to achieve all this with only one message so an exception would need to provide some identifier to the end-user message that as I just said, might be available in different languages.

When I read all the other linked questions I get the impression that an exception message is indeed intended to be read by an end-user and not a developer... a single message is like to have a cake and eat it too.

  • 4
    There are two other questions asked here on Programmers that come to mind. I'm not sure if they are duplicates or not, but I think you should read them and perhaps they'll address some or even all of your concerns: How to write a good exception message and Why do many exception messages not contain useful details? – Thomas Owens Nov 3 '15 at 14:42
  • 2
    @ThomasOwens I've read them both but they don't address the specific receiver of an exception message. They talk about users in general but who is he? Is it the user of the software that has now idea about programming or developers who should analize what went wrong? I'm never sure whom should I address when writing messages. – t3chb0t Nov 3 '15 at 14:46
  • 2
    I think a good answer here may refer to either or both of those questions, but they're far from duplicates. – Lightness Races in Orbit Nov 3 '15 at 15:03
  • 1
    Why on earth was this closed as duplicate? Even if it were a duplicate of that other question (which it's not), this one clearly has a better answer, it should be the other one that's marked as duplicate. – Ben Aaronson Nov 5 '15 at 13:45
  • 2
    @MichaelT I still don't really see much overlap there unless you presuppose that Exception messages are for users – Ben Aaronson Nov 5 '15 at 16:45
46

Those messages are for other developers

Those messages are expected to be read by developers to help them debug the application. This can take two forms:

  • Active debugging. You're actually running a debugger while writing code and trying to figure out what happens. In this context, a helpful exception will guide you by making it easy to understand what's going wrong, or eventually suggesting a workaround (although this is optional).

  • Passive debugging. The code runs in production and fails. The exception is logged, but you only get the message and the stack trace. In this context, a helpful exception message will help you quickly localize the bug.

    Since those messages are often logged, it also means that you shouldn't include sensitive information there (such as private keys or passwords, even if it could be useful to debug the app).

For instance, IOSecurityException type of an exception thrown when writing a file is not very explicit about the problem: is it because we don't have permissions to access a file? Or maybe we can read it, but not write? Or maybe the file doesn't exist and we don't have permissions to create files there? Or maybe it's locked (hopefully, the type of the exception will be different in this case, but in practice, types can sometimes be cryptic). Or maybe Code Access Security prevents us from doing any I/O operation?

Instead:

IOSecurityException: the file was found but the permission to read its contents was denied.

is much more explicit. Here, we immediately know that permissions on the directory are set correctly (otherwise, we won't be able to know that the file exists), but permissions at file level are problematic.

This also means that if you cannot provide any additional information which is not already in the type of the exception, you can keep the message empty. DivisionByZeroException is a good example where the message is redundant. On the other hand, the fact that most languages let you throw an exception without specifying its message is done for a different reason: either because the default message is already available, or because it will be generated later if needed (and the generation of this message is enclosed within the exception type, which makes perfect sense, "OOPly" speaking).

Note that for technical (often performance) reasons, some messages end up being much more cryptic than they should be. .NET's NullReferenceException:

Object reference not set to an instance of an object.

is an excellent example of a message which is not helpful. A helpful message would be:

Object reference product not set to an instance of an object when called in product.Price.

Those messages are not for end users!

End users are not expected to see exception messages. Never. Although some developers end up showing those messages to the users, this leads to poor user experience and frustration. Messages such as:

Object reference not set to an instance of an object.

mean absolutely nothing to an end user, and should be avoided at all costs.

The worst case scenario is to have a global try/catch which throws the exception to the user and exits the app. An application which cares about its users:

  • Handles exceptions in the first place. Most ones can be handled without disturbing the user. Network is down? Why not wait for a few seconds and try again?

  • Prevents a user from leading the application to an exceptional case. If you ask the user to enter two numbers and divide the first one by the second one, why would you let the user to enter zero in the second case in order to blame him a few seconds later? What about highlighting the text box in red (with a helpful tool tip telling that the number should be different than zero) and disabling the validation button until the field remains red?

  • Invites a user to perform an action in a form which is not an error. There are no enough permissions to access a file? Why not ask the user to grant administrative permissions, or pick a different file?

  • If nothing else works, shows a helpful, friendly error which is specifically written to reduce user's frustration, help the user to understand what went wrong and eventually solve the problem, and also help him prevent the error in the future (when applicable).

    In your question, you suggested to have two messages in an exception: a technical one for developers, and the one for end users. While this is a valid suggestion in some minor cases, most exceptions are produced at a level where it is impossible to produce a meaningful message for the users. Take DivisionByZeroException and imagine that we couldn't prevent the exception from happening and can't handle it ourselves. When the division occurs, does the framework (since it's the framework, and not business code, which throws the exception) know what will be a helpful message for a user? Absolutely not:

    The division by zero occurred. [OK]

    Instead, one can let it throw the exception, and then catch it at a higher level where we knew the business context and could act accordingly in order to actually help the end user, thus showing something like:

    The field D13 cannot have the value identical to the one in field E6, because the subtraction of those values is used as a divisor. [OK]

    or maybe:

    The values reported by ATP service are inconsistent with the local data. This may be caused by the local data being out of sync. Would you like to synchronize the shipping information and retry? [Yes] [No]

Those messages are not for parsing

Exception messages are not expected to be parsed or used programmatically either. If you think that additional information can be needed by the caller, include it in the exception side by side with the message. This is important, because the message is subject to change without notice. Type is a part of the interface, but the message is not: never rely on it for exception handling.

Imagine the exception message:

Connecting to the caching sever timed out after waiting for 500 ms. Either increase the timeout or check the performance monitoring to identify a drop in server performance. The average wait time for caching server was 6 ms. for the last month, 4 ms. for the last week and 377 ms. for the last hour.

You want to extract the “500”, “6”, “4” and “377” values. Think a bit about the approach you will use to do the parsing, and only then continue reading.

You have the idea? Great.

Now, the original developer discovered a typo:

Connecting to the caching sever timed out after waiting [...]

should be:

                            ↓
Connecting to the caching server timed out after waiting [...]

Moreover, the developer considers that month/week/one hour are not particularly relevant, so he also does an additional change:

The average wait time for caching server was 6 ms. for the last month, 5 ms. for the last 24 hours and 377 ms. for the last hour.

What happens with your parsing?

Instead of parsing, you could be using exception properties (which can contain whatever one wants, as soon as the data can be serialized):

{
    message: "Connecting to the caching [...]",
    properties: {
        "timeout": 500,
        "statistics": [
            { "timespan": 1, "unit": "month", "average-timeout": 6 },
            { "timespan": 7, "unit": "day", "average-timeout": 4 },
            { "timespan": 1, "unit": "hour", "average-timeout": 377 },
        ]
    }
}

How easy is to use this data now?

Sometimes (such as within .NET), the message can even be translated into user's language (IMHO, translating those messages is absolutely wrong, since any developer is expected to be able to read in English). Parsing such messages is close to impossible.

  • 2
    Good point about the end user. I would add that the end user should not see exception messages for security reasons as well. Knowing the exact nature of an error to a non-layman user can present an exploit. The end user should only know the error in the context of what he or she was doing. – Neil Nov 3 '15 at 15:02
  • 1
    @Neil: that's a bit too theoretical. In practice, the benefit from hiding the logs from the user are outweighed by the complexity of online logging. Not counting the user friendly aspect. Imagine that you are installing Visual Studio on your new Windows VM. Suddenly, the installation fails. Would you prefer just going to the logs and diagnosing the problem yourself (with the help of Stack Exchange and blogs), or wait until Microsoft solves the problem and publishes a hotfix? – Arseni Mourzenko Nov 3 '15 at 15:46
  • 4
    I think everyone is aware that the "object reference..." message is unhelpful. The relevant question though is what machine code would you like the jitter to generate that produces the message you want? If you do this exercise you quickly discover that the message cannot be easily generated without an enormous performance cost imposed on all the non-exceptional cases. The benefit of making a careless developer's job very slightly easier is not paid for by the performance hit to the end user. – Eric Lippert Nov 3 '15 at 20:02
  • 7
    Regarding security exceptions: the less information in the exception the better. My favourite anecdote is that in a pre-beta version of .NET 1.0 you could get an exception message like "You do not have permission to determine the name of file C:\foo.txt". Great, thanks for that detailed exception message! – Eric Lippert Nov 3 '15 at 20:04
  • 2
    I disagree with never showing the end-user a detailed error message. It has happened to me many times that I get a cryptic error message preventing me from starting my game/software, but when I google it I find out there's an easy workaround. Without that error message there'd be no way to find the workaround. Maybe it's better to just hide the details under a "more details" button? – BlueRaja - Danny Pflughoeft Nov 3 '15 at 22:41
2

The answer to this depends entirely on the exception.

The most important question to ask is "who can fix the problem?" If the exception is caused by a file system error while you're opening a file, it may make sense to give that message to the end user. The message may contain more information about how to fix the error. I can think of one definitive case in my work where I had a plugin system that loaded DLLs. If a user made a typo on the command line to load the wrong plugin, the underlying system error message actually contained useful information to allow them to correct the problem.

Most of the time, however, an uncaught exception can't be fixed by the user. Most of them involve making changes in the code. In these cases, the consumer of the message is clearly a developer.

The case of caught exceptions is more complicated because you have an opportunity to properly process the exception and throw a more friendly one. One scripting language I wrote threw exceptions whose message was very clearly intended for a developer. However, as the stack unwound, I added user-readable stack traces from within the scripting language. My users very rarely could grok the message, but they could look at the stack trace in their script and figure out what happened 95% of the time. For the remaining 5%, when they called me, we had not only a stack trace, but a developer-ready message to help me figure out what was wrong.

In all, I treat the Exception message as an unknown content. Someone further upstream, who knew more about the implementations, has handed my software an exception. Sometimes I have a hunch that unknown content will be useful to an end user, sometimes it's not.

  • 1
    "If the exception is caused by a file system error while you're opening a file, it may make sense to give that message to the end user": but when you throw the file system error, you don't know the context: it might be the user action, or something completely unrelated (like your app making a backup of some configuration, without the user even noticing it). This implies that you'll have a try/catch block which shows a error message, which is very different from showing the exception message directly. – Arseni Mourzenko Nov 3 '15 at 20:10
  • @MainMa You may not know the context explicitly, but there's tons of hints. Like, for instance, if they're in the process of opening a file, and the exception is "IOError: permission denied," there's a reasonable chance the user can put 2 and 2 together and figure out the file in question. My favorite editor does this -- it just outputs the exception text in a dialog box, no fluff. On the other hand, if I was loading up my application and got "permission denied" while loading a bunch of images, it is much less reasonable to assume the user can do something with the information. – Cort Ammon Nov 3 '15 at 21:34
  • Arguing for your point, I've never seen any value in showing a user a NullReferenceException, other than that the mere act of displaying that message shows that your software didn't have a clue how to recover! That exception message is never useful to an end user. – Cort Ammon Nov 3 '15 at 21:36

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.