RFC: Revamp how compile time errors are shown

The Problem

Right now some errors can be unwieldy to read for a few reasons

  • It shows all frames. This can lead to pages of text that looks overwhelming and makes it hard to the error.
  • It is not always clear what the error is or where it is located

The Proposed Changes

  • By default, just show the code with the error, not the entire stack trace. This is what Elm does and it works quite well. Helps focus on what is necessary
  • Add a command line option and/or ENV to show the full trace. Sometimes it is useful to see more, but let’s make that optional instead of making the current verbose output the default. Example ``
  • Some changes to how the error is displayed to make it easier to scan at a glance. Code + pics attached

Example Code and Screenshots

Implementation Details

For errors without more info (just one line)

  • Prefix with “Error”
  • Print error message in yellow and bold
  • (not sure about this) show how to print the full trace (like it does now)
  • Show what file it is
  • Show line number right by the source code line
  • Show code line (maybe bolded?)

More screenshots at https://github.com/paulcsmith/crystal-errors

For errors with more than one line

  • Print first line in yellow just like other errors
  • Everything after first newline goes after the source line.

Macro errors

  • Print surrounding lines
  • Only bold line with the problem
  • Show the expanded code, and underneath it show the unexpanded code

It is important to show both. The unexpanded part show what the user wrote. The expanded part helps figure out why the issue might be there. Use --verbose/–debug for more in-depth look if it still is not enough

Not Sure About

  • Not sure about including the dim line that says how to show full trace. Maybe not useful enough?
  • Should source code line be bolded? Maybe too much is going on.

Who would do this work?

I wish I had more time for this, but with all the stuff with Lucky I just don’t have time. I’m happy to help where I can but I likely won’t be able to help with code.

I really want this feature though and will open up a Bounty Hunt to help out if this gets approved :D


I recently started to code in Elm and I love the error messages. They look like a human is talking to you, not a machine. And I’d like Crystal to speak the same language.

That said, in Elm it’s easier. Everything is pretty much explicitly typed. There are no overloads. There’s no mutation of variables or automatic computation of unions. There are no macros. Etc., etc.

Still, we can improve our error messages. I just wonder if removing all the traces will make it harder to understand what’s going on, but we can try it.

I can work on this, though my time to do it is also pretty tight. But given that I wrote most of the code for errors it’ll be easier for me to do it.

I like the name --verbose, though right now it’s taken and use to display the linker command. We can probably rename that (the existing flag) to something else.

Then, instead of “type must be String, not Int32”, which looks robotic, I’d like to see it more Elm-ish, like “You told me foo returned String but it seems to be returning Int32”, and so on. Instead of “undefined constant Foo” maybe “I can’t find a type or constant named ‘Foo’”. Instead of “undefined method ‘not_a_method’ for String” maybe “I can’t find an instance method ‘not_a_method’ defined in String”. And so on.

What do you think?

If we do this, I’ll start improving the lexer (syntax) errors first.


Improving messaging

Yes! I would love Crystal to have more Elm like error message, and I love your idea to make it more human like.

Your examples are awesome. I personally prefer "Crystal" over "I", e.g. Crystal can't find the type or constant 'Foo'

I think improving syntax errors would be great too. I’d love to help with that if you need it

CLI Flag --verbose

As for the flag, maybe instead of --verbose --all-frames or --full-trace? I also think an ENV var might be nice too.

Not enough info with last frame

If we find that the last it is not enough information most of the time, I can work on a design that still puts emphasis on the last frame so it is clear that’s the main one to look at. Right now they all look the same so it can make it tricky to find what part is the problem

My initial though is keep the error message yellow and add some kind of delineation around it: -- Error: Some error --

Future ideas

I also had ideas on how to improve the overloads errors, but I wanted to focus mostly on the design and information overload. I’d love to help where I can though. Please feel free to ping me on any PRs if you need/want some design or language input!


Would it be ok to move this to an issue on Crystal-lang with a draft plan of action? Then I can add a bounty on bounty source. I am really excited to have improved error messages :D


In the end, is crystal who is speaking so it’s like itself talking in 3rd singular. There are some people that do that though. Another pro for I is that it’s the shorter it can get.

--trace-error sounds like a good option for me. Like asking the compiler to trace the error.


1 Like

https://github.com/crystal-lang/crystal/issues/7553 with a bounty!

1 Like

I’ve been exploring Rust a lot recently and one of the things I’m coming to love a lot is its error messages. I’ll take the example of a missing method:


struct Foo { value: i32 }

fn main() {
  let foo = Foo { value: 1 };

Error with rustc err.rs:

error[E0599]: no method named `bar` found for type `Foo` in the current scope
 --> err.rs:5:9
1 | struct Foo { value: i32 }
  | ---------- method `bar` not found for this
5 |     foo.bar();
  |         ^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.

Compiler errors are given a code which you can then look up using rustc in your terminal, or browse them on the compiler error index, which showcases reproduceable examples of each error and suggestions on how to fix them:


Here’s a more complex example that I really like:

I don’t suggest that we strive to implement exactly what Rust has, but I think there’s a lot to be inspired by - particularly I think the compiler error index and something like rustc --explain.


Very cool. I find the formatting of Rust’s errors to be a bit overwhelming, but I do LOVE the --explain command. I also like the example you posted from Twitter that helps with the Syntax message. That kind of thing is super helpful