What makes a good error message? As developers, we certainly see a lot of them, and they are almost always the first indication that Something Bad (tm) has happened. So what makes a good one?
In order to answer that, it's probably best to illustrate what they are FOR. There are generally 2 classes of error message:
- Messages intended for users. These messages indicate that the USER has done something wrong. Typically bad input.
- Messages intended for the developers. These are TYPICALLY cries, bleats, or yelps for help to tell the programmer that something unexpected has happened.
Since this isn't "Adventures in Usering", I'll focus mainly on the latter.
So what makes a good error message? I've given this a bit of thought lately, and I think that a really good error message has the following 4 qualities:
- It tells what was expected.
- It tells what it actually got, and how or why that is different than what was expected.
- It tells how or why this might have happened.
- It gives some direction on how to fix the problem.
As an example of a good error message, let's look at some pro and counter examples.
The ultimate counter example of all 4 qualities can be seen here
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandlerAwesome. It tells me, as a developer, absolutely nothing useful to find out what the heck happened, what was SUPPOSED to happen,
where it happened, or what I'm supposed to do about it. This particular nugget occured while starting up an application server. Since it is java code, I was able to see the stack trace that led to it, so I had a
very small clue as to where to start looking.
This is an example of a very common type of error message; the "I'm an error message" message. We've all seen these; they provide no useful information other than the fact an error occurred. A message announcing that an error occurred is obviated by the fact the message is there at all. I used to joke in college that I was going to write a compiler and to save message space, it would have one error message that said simply, "No". I didn't think so many developers would steal and stylize my idea, but apparently they have.
So what would have been better?
Tell me what was expectedIn this case, this error was the result of a missing file that was needed during some startup code. So, tell me that.
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandler. Expected to read from file: /opt/apps/config/i18n.xmlNOW we're getting somewhere. Even with THIS bit of info, I might have an idea by putting together that there IS an error message, and that it's saying that it's expecting to read from a given file, that maybe something is wrong with that file. My years of experience might lead me to see if that file exists, its permissions are such that it can be read by the app, it looks "ok" (not zero length, the format matches what it probably should (xml), etc.)
Tell me what actually occurredNow that we know what the app wanted, an indication of what differed from this expectation would be nice.
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandler. Expected to read from file: /opt/apps/config/i18n.xml, but file was not foundHey now, this looks promising. And spares me the checks I would have had to make if this tidbit were missing, as before. Too, it disambiguates from something like...
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandler. Expected to read from file: /opt/apps/config/i18n.xml, but file is not readableTell me how this happenedUnfortunately, this isn't always possible, and sometimes the first 2 bits of information are more than enough to get started. But when writing error messages, if you have any idea of how the error could have occurred, put it in the message.
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandler. Expected to read from file: /opt/apps/config/i18n.xml, but file is not readable. This can occur if permissions are set incorrectly on the file or its directory. This happens often during an initial setup of the application, since this file is deployed separately from the application itself.Be careful here, this
might lead a developer or tester to the wrong conclusion. When writing error messages, if there are a litany of reasons an error might occur, I will often leave this step out since it would be too much to list them, and just listing some might cause one to not check the others.
Tell me how to fix itThis one can also be tricky, since it's often not known how to fix the problem, especially when the error message is first being coded. However, I've found that over time developers will tend to see a certain error and know what causes it. If possible, that's the time to go back and fix the message (if possible, and it often isn't). Or of course, sometimes an error can only be fixed in a limited number of ways. So, list them.
Exception data: com.stuff.SystemException: Failed to initialise or configure service: config class com.stuff.ConfigurationImpl, prefix DefaultHandler, serviceClassName com.stuff.DefaultServiceExceptionHandler. Expected to read from file: /opt/apps/config/i18n.xml, but file is not readable. This can occur if permissions are set incorrectly on the file or its directory. This happens often during an initial setup of the application, since this file is deployed separately from the application itself. Check the permissions on /opt/apps/config/i18n.xml to ensure they are readable by the application.Although I've gone a bit off the deep end on this particular example, if I'd had this information on this very real message (but somewhat obfuscated to protect the perpetrators of such heinousosity), it would have saved a lot of debugging time. In this particular case, the application reads a file that lists what OTHER files it should read, and it was one of those second level files that was missing. Because the error message was so vague, I had to step-debug into the code at that point, and check EACH LOOP ITERATION until I found the offending iteration and fix the file it couldn't find. This took the better part of a half hour, and it wasn't the first time, so I knew where to start. Two minutes by the original programmer would have saved me and coworkers of mine hours of debugging time.