The Art of the Good Error Message
This is a blog about the development of Yeller, The Exception Tracker with Answers
ArgumentError: When assigning attributes,
you must pass a hash as an argument
Assert failed: Value args to hash-map must be generators
(every? generator vs)
What’s wrong with these error messages (and thousands like them)?
What makes them much harder to debug, especially if they came out of library you’re using that’s wrapping whatever’s causing the errors? If the stacktrace points 20 levels deeper than your code, sure, you can dig in, download the source and understand all the layers, but it’d sure be nice if you didn’t have to.
Why was this coffeescript error message all over StackOverFlow?
[Mon Jun 08 2015 10:19:02 GMT+0100 (BST)] ERROR Unable to load /Users/tcrayford/proj/yeller/hubot/scripts/earl: SyntaxError: unmatched OUTDENT
at exports.throwSyntaxError (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/helpers.js:197:13)
at Lexer.exports.Lexer.Lexer.error (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/lexer.js:778:14)
at Lexer.exports.Lexer.Lexer.pair (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/lexer.js:690:16)
at Lexer.exports.Lexer.Lexer.outdentToken (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/lexer.js:378:16)
at Lexer.exports.Lexer.Lexer.closeIndentation (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/lexer.js:550:19)
at Lexer.exports.Lexer.Lexer.tokenize (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/lexer.js:34:12)
at exports.compile.compile (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/coffee-script.js:35:36)
at Object.loadFile (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/coffee-script.js:176:14)
at Module.load (/Users/tcrayford/proj/yeller/hubot/node_modules/hubot/node_modules/coffee-script/lib/coffee-script/coffee-script.js:211:36)
What if every error message you ever saw as a developer told you exactly what the issue was?
Not just why a thing broke, but what you were doing to break it.
CONTEXT RULES EVERYTHING AROUND ME
The fix to these error messages is simple: include context about what was going on.
What if the rails error message above looked like this:
ArgumentError: When assigning attributes,
you must pass a hash as an argument,
but you passed #<User id:12345 email:tom@example.com>
This takes a tiny bit of extra effort for the developer when writing the error message, but it’ll pay off thousands of times - once each time somebody hits it and gets confused about what they’re doing wrong.
Having great error messages is especially important for folk new to your library or framework - errors can be awfully frightening even if they are well designed, so it’s important to make them as good as possible.
Clojure folk: use ex-info
This bit is Clojure specific, but if you’re in Clojure, use the ex-info
function for constructing your exceptions as much as possible, and add the context in the map:
(ex-info "some error message" {:wrong-args [1 2 3]})
If the tool you use for tracking exceptions in production or printing exceptions at the repl understands clojure, it’ll print out the ex-data
, giving you all that context. If you use ex-data
, you can also manipulate it just like you do any other kind of data, which can get especially useful when debugging the more complicated errors.
How To Apply This to your App
Next time you raise
or throw
an exception, look at any local variables or arguments you’ve got at hand and try to include them in the error message, or in ex-data
. It’ll save the person debugging it a whole bunch of pain, and doesn’t take very long to do. And, if you run across a raise
or a throw
that doesn’t have the extra data, just add it in there.
I know I’ve written my fair share of bad error messages that missed out context in the past, and it’s only come back to haunt me in the future. It’s a nice easy win for your future self, and your users.
This is a blog about the development of Yeller, the Exception Tracker with Answers.