The Art of the Good Error Message

This is a blog about the development of Yeller, The Exception Tracker with Answers

Read more about Yeller here

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.


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>

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.

Read more about Yeller here

Looking for more about running production applications, debugging, Clojure development and distributed systems? Subscribe to our newsletter: