This is my take on what error messages should convey and what issues I see every day.
Introduction
One of my biggest pet-peeves these days are error messages that are completely useless or lacking any useful information. I'm sure you see them all the time and as a developer, you might even display useless error messages yourself.
Full disclosure, I'm guilty of doing this myself, but I strive everyday to improve what I am doing.
I'm hoping this article will help you think about the error messages you're showing and maybe changing your error messages to give the user (and developer) more information in order to make them more useful.
Error messages should tell the user (and the developer/support) what went wrong, why it went wrong and where it went wrong. Most developers get the first part, but fail on the second and third parts.
I'll be using Visual Studio and my own code as examples of error messages and how they can be improved.
Visual Studio has a lot of really useless messages, but it is far from alone. Over the years, I've been striving to update all my error messages in my applications to be a lot more useful. You don't have to necessarily go back and refactor all your error messages, but going forward, you should consider what you're returning/displaying.
Why are Error Messages Important?
Error messages give us additional information when something goes wrong or the user did something they shouldn't do. This is important so that we can correct our input as a user, or find and fix a bug as a developer.
Clear and informative error messages help both the user and the people supporting the software. Granted a lot of users don't read the error messages and simply click "OK" when a message pops up, but at least you tried to tell them what went wrong.
Vague error messages don't help anyone and only frustrate users as they don't know if it's something they did or if it's a bug in the software.
Let's Go!
There are three main things the an error message should tell you.
What Went Wrong
Something went wrong, what was it? Most developers get this right most of the time, but there are plenty of examples of really bad error messages. Sometimes, the error messages only make sense to the developer or the error message is so generic that it's useless.
Why It Went Wrong
Most developers don't bother telling you why the error occured. In some cases, you might not know why the error occured, but if you do, add it to the error message. Couldn't save the file? Why not? Was it a permissions issue? Out of Disk Space? Invalid Filename?
Where It Went Wrong
When it comes to solving problems, it's very important to know WHERE it went wrong. If you don't know where it went wrong, it's a lot harder to help the user correct their mistake or find and fix your bug.
A lot of times, this information is easily determined because the error message is close to the origin. I click a button on a form and the error message pops up. My context is pretty clear, it's related to what the code for the button did. However, the deeper the error, the more important the context becomes. If the code from the button calls code that calls code that calls code, etc., the context becomes less and less clear.
Optionally, if you can, give them help on how to correct the error if it can be determined.
Error messages should be as clear and concise as you can make them. If the information doesn't help with the what, why or where, it probably shouldn't have that information in the error message.
Examples of Bad Error Messages and How We Can Fix Them
Example 1
This error message only tells me WHAT happened, and fails to tell me WHY it happened or WHERE it happened.
While it's nice that it told me that the operation couldn't be completed, it fails to give me some very important information. "The operation", OK, what operation are we talking about that couldn't complete? This was something in the background that I had no control over so I don't even know where to start to find the problem.
Then it says it couldn't be completed. Well since I don't know which operation it's talking about, I have no idea why it failed.
- Was it missing information?
- Was the information it was using incorrect?
- Did it run out of resources?
- Was the operation related to Visual Studio or a 3rd Party?
There are way too many questions that error message creates that the error message is essentially useless to me since I have no way to figure out what went wrong or how to fix it.
At a minimum, the error message should at least tell me what operation failed. If it could answer any of the questions I asked above, then it's going to be easier and faster for me to track the problem down.
Example 2
This is one of my favorites because I wasn't doing anything that required a connection to anything. This was obviously a background task in Visual Studio and I'm pretty sure it was related to Xamarin and the connection to my MacBook for iOS development (even though I wasn't in a Xamarin project at the time).
Like most errors, this error message only tells me WHAT happened, and fails to tell me WHY it happened or WHERE it happened.
This error should answer some of these questions:
- What server was it connected to?
- What was trying to connect to the server?
- Is there a reason it was lost?
If you don't give me enough information to do something about the error, then why show the error at all? Other than telling "something" went wrong, it's of no use.
Example 3
In this example, I happened to know the context of the error because I was getting the latest files from source control. So at least with this error message, I knew WHERE it happened. It still leaves me with additional questions that may or may not be easy to answer.
In the world of multi-threaded code, it might be difficult to generate a useful error message because you're on the outside just waiting. You're waiting on a process to finish, but you don't know where it's at or why it's not finished yet.
If you can design your long processes to track where it's at and what it's doing, please do.
The questions I would like answered here are:
- What operation is it doing?
- What files are being affected?
Example 4
Take this example from my code:
e.ErrorText = "This type of file is not allowed.";
This is from code for one of the websites I did. The context is given because there is only one place to upload the file on that page, so the WHERE was answered. Had I included that information in the error message, it would have been redundant and added nosie that wasn't helpful. The WHY is answered because they are trying to upload a file with a type that isn't allowed.
There are two things I can do to improve this error message. The first is to show them the name of the file they are trying to upload, or at the minimum the type of file they were trying to upload. The second thing I can do to improve the error message is to give them the types that they are allowed to upload.
If someone get's this error message, the first question they ask might be "OK, what am I allowed to upload?" By showing them a list, they can then get immediate feedback on that question.
By displaying the filename, it can also help support. A lot of users will take a screen shot of the error message and send it to support. If I can see they are trying to upload "windows.exe" and I only accept .PDF files, then I can tell them what they are doing wrong. If I see they are trying to upload "untitled.pdf" and I accept .PDF documents and it is telling them that it's not allowed, I now know I have a bug in my code that I need to fix.
So the error message would be better suited to say:
e.ErrorText = $"This type of file \"{fileExtension}\" is not allowed.
You are only allowed to upload .pdf files";
Example 5
A common validation is to check the length of something like this:
This is a made up example as I always include everything that is needed in the errors in cases like this. However, I see these types of error messages all the time and want to illustrate it here.
if (lastName.Length > 25)
{
e.ErrorText = "Invalid Length";
return;
}
There are several problems with this error.
The first problem is context. What has an invalid length? We know what it is in the code because we are testing for it. To give it more context, we would change the error message to "Last name is too long
".
That still leaves the question of how long is too long? We should also provide that information in the error message. So now the error message should be: "Last name cannot exceed 25 characters
".
Now if you want to really help them out, you can do something like:
ErrorText = $"Last name cannot exceed 25 characters.
The last name you entered was {lastName.Length} characters long.";
If the length of something has to be between 2 values (say 5 and 10 characters), then you should include that information in your error message as well.
Example 6
A common validation is to check the range of values of something like this:
This is another made up example to illustrate a point.
if (volumeLevel < 0 || volumeLevel > 100)
{
ErrorText = "Value out of range";
}
Like the length example above, there is no context about what the error message relates to. There is also no indication of what the value is or what range it expects to fall into.
A better error message would be:
if (volumeLevel < 0 || volumeLevel > 100)
{
ErrorText = $"The volume level {volumeLevel} must be between 0 and 100";
}
This would tell the user (and the support person) what the value was and what it expected. If for some reason the volume level was -5, that might be considered a bug in the program that needed to be fixed.
If you cannot determine the context of where that error message came from, you can always add additional context to the error message. Maybe this check was done when you were trying to set the current volume level, in which case you could do this:
if (volumeLevel < 0 || volumeLevel > 100)
{
ErrorText = $"Error while trying to set the volume.
The volume level {volumeLevel} must be between 0 and 100";
}
Final Thoughts
Error messages are only as good as the information they provide. In some cases, you don't need to give very detailed information if the what, why and where can be easily determined.
When I create an error message, I ask myself "What information do I need to resolve this error?"
If I intercept an Exception that I wasn't expecting, the information most important to me is going to be a callstack if it's available. At a minimum, I want to know the actual error message from the exception.
So my error message might be something like:
catch (Exception ex)
{
return $"Unable to save the connections settings file. Error was '{ex.Message}'";
}
If I'm processing a bunch of objects and something goes wrong, I need to know which object it was processing at the time of the error so I try to include any information that might identify the object (Database ID, Customer Name, something...).
When I create something like an API where the error messages might be similar (say "Login Failed
"). I might keep the error message vague to the user as they might not need to know all the details of why the login failed. What I will do is add a unique number to the error message so that I can go in and find the cause of failure. So the error message might be "Login Failed (-100)
". I can then go into my code and find the -100
and know why it failed.
If your error message is related to the parameters passed in, try to show the values of those paramaters if possible.
Random Useless Visual Studio Error Messages
History
- 14th January, 2022: Initial version