Long Error Messages(blog.appsignal.com)
blog.appsignal.com
Long Error Messages
https://blog.appsignal.com/2021/09/22/improve-your-life-with-long-error-messages.html
6 comments
Apple in particular very much has a culture of "errors don't happen" and try to gaslight you when they do. Stuff like having zero insight into sync processed and Apple Music just silently skipping to the next song if it fails to stream.
Microsoft is similar in a way where when errors do happen, they usually log them or give you a pop up, but it'll be some inscrutable thing like error -1002, with nothing resembling a backtrace or other information to figure out where it's coming from. It's the number 1 reason why I hate debugging any problem on windows compared to linux.
There's another great example of this in maps, where it will refuse to let you favourite your home or work address if it's not in the right format. It won't even tell you that it failed - it doesn't do it and the interface doesn't make it obvious that it didn't do it. So you're out of luck if you want to figure out _why_ it isn't working.
In my case I had a country name accidentally put in a region field, but it didn't look out of place because field labels are missing when filled in. Frustratingly, your contact card with that address will show a map preview that when tapped opens maps with a pin in the right place, so there's clearly some logic to "fix" incorrect addresses somewhere.
In my case I had a country name accidentally put in a region field, but it didn't look out of place because field labels are missing when filled in. Frustratingly, your contact card with that address will show a map preview that when tapped opens maps with a pin in the right place, so there's clearly some logic to "fix" incorrect addresses somewhere.
I got stuck in a loop when trying to install Big Sur on my old Macbook Air. It would pretend to install, then reboot the laptop. When it came back up, nothing would have changed, and no error messages in sight. After several rounds of this I figured out it was failing because there was too less free space on the disk.
While I generally don't have much love for Oracle, this is a thing they do well. All the messages are numbered like ORA-123456 and are dead easy to google without mixing in any other results. You also sometimes get an error message containing relevant parameters.
Microsoft does acceptably wel with their 0x80071234, but no parameters, messages like 'installer failed', and googling them gives 10 zillion sites with crap results.
Microsoft does acceptably wel with their 0x80071234, but no parameters, messages like 'installer failed', and googling them gives 10 zillion sites with crap results.
This is a GREAT idea! I'd love to see this happen.
The evolution of better error messages has been one of the biggest QOL improvements in language and API design of late. For instance, even a system as intricate and demanding as Vulkan can be pleasant to work with, because when something goes wrong you always know exactly what it is, and you have a link to the documentation to read more about it.
And the best thing is that when you are releasing, you turn this validation layer off, which is good for 2 reasons:
-No performance is wasted in release builds
-Validation checks can be expensive and exhaustive as it will not run in production code
-No performance is wasted in release builds
-Validation checks can be expensive and exhaustive as it will not run in production code
Looking at this more from the non-technical end user perspective, my rule for error messages is that they should always be actionable. If the viewer is the developer, then technical details are important. But if the viewer is an end user/consumer, they don't necessarily need to know exactly what happened, but they do need to know what to do.
For an average end user...
* If the hard disk is full, that's easy to convey and easy to suggest a fix.
* If a hardware fault occurs on activating a device, suggest they try again and contact support if the problem persists.
* If an unhandled exception occurs, all they need to know is that something very unexpected happened, and they should contact support - they don't need to see a stack trace and error message.
For an average end user...
* If the hard disk is full, that's easy to convey and easy to suggest a fix.
* If a hardware fault occurs on activating a device, suggest they try again and contact support if the problem persists.
* If an unhandled exception occurs, all they need to know is that something very unexpected happened, and they should contact support - they don't need to see a stack trace and error message.
This cannot be overstated.
Every time I review error messages, I _always_ ask 3 things:
- What: is the "what" clear.
- Why: explain the reason this happened.
- Next: what can be done next.
Almost all error messages can be expressed this way (the exception being the ones about security where giving information is a way for attackers to more about users, etc.)
The result is great: users almost never ask you any questions, and when they do, it usually means it's either because the message told them to, or because the error message is not clear, and we ensure to clarify it.
Every time I review error messages, I _always_ ask 3 things:
- What: is the "what" clear.
- Why: explain the reason this happened.
- Next: what can be done next.
Almost all error messages can be expressed this way (the exception being the ones about security where giving information is a way for attackers to more about users, etc.)
The result is great: users almost never ask you any questions, and when they do, it usually means it's either because the message told them to, or because the error message is not clear, and we ensure to clarify it.
I agree fully with this. Sadly it seems like entirely too much software these days has pivoted to the essentially useless “oops something went wrong” error messages.
One thing I've never understood is why stack traces don't, at a bare minimum, contain the values of the arguments passed to the function that threw the exception. That would be immensely helpful for reproducing the error.
Even better would be including the current values of any local variables introduced within the function, and any global variables referenced within the function.
Even better would be including the current values of any local variables introduced within the function, and any global variables referenced within the function.
That would require each argument be printable (i.e., has a function that prints its contents in a human-readable format), which is not always a given. Also, trying to print an object may cause another exception. (Imagine what will happen if you have a linked list, and a printer function that follows the links and print all nodes, but now the list is circular.)
That's a good point. I would also like very much if languages included a sensible and useful default implementation of toString() or the equivalent for all types (e.g. print all the object's private & public property names & values, recursively, stopping if cycles are found).
Of course, this is probably only remotely feasible in languages that can guarantee that toString() won't have any side effects.
Of course, this is probably only remotely feasible in languages that can guarantee that toString() won't have any side effects.
You need "side effects" to include memory allocation, seg faults, and "significant" amount of computation time (including never terminating). Worse, you need these properties when the program is known to be in an error state.
For a general purpose programming language this means that your toString cannot be a normal function.
For a general purpose programming language this means that your toString cannot be a normal function.
Also good points, I suppose the printing mechanism couldn't use the type's implementation of toString() and would instead have to use an implementation built into the runtime. Still, even being able to see just the raw bytes would be better than nothing in a lot of cases.
This could be very dangerous, depending where an exception happened. Variables can contain critical secrets, like your TLS keys or a user's password and if your stack trace printer isn't aware of what should be redacted now you've got that in your logs.
Agreed, but this would help force developers to deal with treating secrets with the care they should have been treating them with all along, in my opinion.
PowerShell's [SecureString] type is something of a step in the right direction, although its execution is lacking in some respects.
In any case, stack traces should _always_ be treated as secrets.
PowerShell's [SecureString] type is something of a step in the right direction, although its execution is lacking in some respects.
In any case, stack traces should _always_ be treated as secrets.
[deleted]
You can sort of do this by taking a dump of the thread/process. Just print out the location of the dump to the log (main downside being you need a debugger to analyze these files).
pytest stack traces include function arguments for select stack traces. It's stupendously convenient, but it does make the stack traces rather verbose at times.
Next python release (3.10) made a lot of noise because it introduced pattern matching.
But I'm very excited about this upcoming version for a very different reason: they made a huge work on error messages!
Check this out: https://docs.python.org/3.10/whatsnew/3.10.html#better-error...
Syntax errors are more precisely reported, AttributeError/NameError suggest a spelling correction, IndentationError will pinpoint where you screw up...
And yes, a good IDE already does that for you, but a lot of Python dev are using a ligth text editor or just a shell/notebook.
This, to me, is progress! Unsexy, warmly welcome progress.
But I'm very excited about this upcoming version for a very different reason: they made a huge work on error messages!
Check this out: https://docs.python.org/3.10/whatsnew/3.10.html#better-error...
Syntax errors are more precisely reported, AttributeError/NameError suggest a spelling correction, IndentationError will pinpoint where you screw up...
And yes, a good IDE already does that for you, but a lot of Python dev are using a ligth text editor or just a shell/notebook.
This, to me, is progress! Unsexy, warmly welcome progress.
> Syntax errors are more precisely reported, AttributeError/NameError suggest a spelling correction, IndentationError will pinpoint where you screw up...
Helpful, but I can't help but think of Java's syntax errors when you forget a semicolon. If the compiler knows what you meant, then it knows what you meant. That points to the syntax having extra baggage it doesn't really need.
Helpful, but I can't help but think of Java's syntax errors when you forget a semicolon. If the compiler knows what you meant, then it knows what you meant. That points to the syntax having extra baggage it doesn't really need.
It doesn't know. You can identify a wrong answer without knowing the right one.
I can tell you 1 + '1' is wrong in python, but can't tell what you meant.
I can tell you 1 + '1' is wrong in python, but can't tell what you meant.
[deleted]
Sometimes it has a pretty good idea.
Typo correcting is an excellent example of this. I don't want my program to compile and run if I wrote "taxis" when I meant "taxes". I want an error. Taxis is wrong. But if the diagnostic says "Did you mean taxes?" that's great because it calls attention to the most likely mistake.
I'd say if the machine has a suggestion that it knows would work (but could be wrong) it's better to mention what that suggestion is than keep silent, but it would be worse to just press on as if that's what you wrote.
Typo correcting is an excellent example of this. I don't want my program to compile and run if I wrote "taxis" when I meant "taxes". I want an error. Taxis is wrong. But if the diagnostic says "Did you mean taxes?" that's great because it calls attention to the most likely mistake.
I'd say if the machine has a suggestion that it knows would work (but could be wrong) it's better to mention what that suggestion is than keep silent, but it would be worse to just press on as if that's what you wrote.
In my case I assign each error message a unique code and prefix the message with the name of the function. Makes it very easy to find where things went wrong.
For web services, and similar with errors that users see, but where the backend lives closer to the programmers, I very much endorse:
1. Mint a UUID for each specific occurrence of an error the user is getting informed about.
2. Write the whole stacktrace to a log, with the UUID and any other context that seems important.
3. Show the user the UUID, but in smaller type than any advice they might actually be able to use themselves like "0123456789 is not an email address" or "This service is not available to users in your group, juniorAssistants".
4. Ensure front line supports know they need this UUID to pass errors on to anybody who can do more than recite the help text.
Users will take photos of screenshots, they'll attach them to email as Excel spreadsheets with an embedded image, but they can get you the UUID and that shortcuts so much of the debugging process in many cases, while not giving them a bunch of debugging output that:
* Might be non-public by GDPR type rules, legal privilege or just common sense.
* May contain valuable commercial secrets, one man's obvious class hierarchy is another man's secret sauce product design.
* Is long and complicated and will not survive being captured to a screenshot, scaled, pasted into a Word document and then photographed with a phone, which your users actually will do for some reason.
1. Mint a UUID for each specific occurrence of an error the user is getting informed about.
2. Write the whole stacktrace to a log, with the UUID and any other context that seems important.
3. Show the user the UUID, but in smaller type than any advice they might actually be able to use themselves like "0123456789 is not an email address" or "This service is not available to users in your group, juniorAssistants".
4. Ensure front line supports know they need this UUID to pass errors on to anybody who can do more than recite the help text.
Users will take photos of screenshots, they'll attach them to email as Excel spreadsheets with an embedded image, but they can get you the UUID and that shortcuts so much of the debugging process in many cases, while not giving them a bunch of debugging output that:
* Might be non-public by GDPR type rules, legal privilege or just common sense.
* May contain valuable commercial secrets, one man's obvious class hierarchy is another man's secret sauce product design.
* Is long and complicated and will not survive being captured to a screenshot, scaled, pasted into a Word document and then photographed with a phone, which your users actually will do for some reason.
how do you keep from colliding with an existing code when writing a new one, just search the repo for its use before committing or...?
All codes are kept with a constant name prefixed with E_ in one file.
That must be a joy to edit. "Oh, a new error. Time to recompile the whole system!"
It's not an uncommon pattern: https://github.com/postgres/postgres/blob/master/src/backend...
I wasn't making any statement about how common it was, only about how impractical it was.
And soon enough you'll find that it is just more convenient to reuse a few error codes here and there, and soon enough applications just report "error -1: resource failure" or "internal failure" or some other meaningless thing. Far better to just return a string.
And soon enough you'll find that it is just more convenient to reuse a few error codes here and there, and soon enough applications just report "error -1: resource failure" or "internal failure" or some other meaningless thing. Far better to just return a string.
> Section: Class 57 - Operator Intervention
> # (class borrowed from DB2)
> Section: Class 72 - Snapshot Failure > # (class borrowed from Oracle)
Nothing from SAP / ABAP ? :^)
> Section: Class 72 - Snapshot Failure > # (class borrowed from Oracle)
Nothing from SAP / ABAP ? :^)
Considering that Apple, with their large market cap and userbase only has a half-assed version of this, I won't get my hopes up.