Logo of AppSignal

Menu

Reading and understanding Ruby stack traces

Jeff Kreeftmeijer on

When an error happens in your application, Ruby raises an exception and prints the stack trace to the log. In this article, we’ll take a look at how to read the stack trace and how to use it to locate the source of an exception in your application.

The call stack

Whenever you call a method, Ruby places a stack frame on the call stack (or “runtime stack”, but often just called “stack”). The stack frame is a memory allocation that holds the method’s arguments, some space for internal variables, and the return address of the caller.

1
2
3
4
5
6
# divide.rb
def divide(a, b)
  "Dividing #{a} by #{b} gives #{a / b}."
end

puts divide(8, 4)

When one method (divide) calls another method (Fixnum#/, or / for short), the latter is put on top of the stack, because it needs to execute before the first one can finish. When calling divide(8, 4) in this example, Ruby will execute the following actions in order:

  1. Call 8./(4)
  2. Collect the division’s result (2), and put it in a string
  3. Collect the string ("Dividing 8 by 4 gives 2"), and print it to the console with puts

Stack traces

The stack trace (usually named “backtrace” in Ruby, but also referred to as “stack backtrace” and “stack traceback”) is a human-readable representation of the stack at a specific moment while running your program. The stack trace for the call to the / method would look like this, showing it was called in the divide method on line 2, which was called in the <main> method on line 5.

1
2
3
divide.rb:2:in `/'
divide.rb:2:in `divide'
divide.rb:5:in `<main>'

In the example above, calling our divide method with a 0 as one of its arguments will result in a ZeroDivisionError exception.

1
2
3
4
5
6
# divide_by_zero.rb
def divide(a, b)
  "Dividing #{a} by #{b} gives #{a / b}."
end

puts divide(8, 0)

When that happens, Ruby will print the exception, along with the stack trace, to the console. The stack trace shows the stack in a human-readable format, with each method’s location in your code, to help you pinpoint where the exception came from.

1
2
3
4
$ ruby divide_by_zero.rb
divide_by_zero.rb:2:in `/': divided by 0 (ZeroDivisionError)
        from divide_by_zero.rb:2:in `divide'
        from divide_by_zero.rb:5:in `<main>'
  1. On the first line in the stack trace above, we can see that a ZeroDivisionError was raised with “divided by 0” as its message. Besides the exception itself, we can see that it happened on divide_by_zero.rb:2:in `/', which means the error was raised from the second line of our example file, from a method named / (which is Fixnum#/, because the first argument is the Fixnum 8).

  2. The stack trace’s second line shows where the / method was called from. In this case, it’s from our divide method on line 2.

  3. The last line shows that divide was called from <main>, which refers to the initial context of a Ruby application. Generally, it means it was called from outside any “real” method.

NOTE: In Ruby 2.5, the logger prints the stack trace in reverse to fit them in a terminal window. The last line shows the exception, preceded by the line the exception happened on. The lines above that are the route up the stack.

Understanding stack traces

The stack trace gives you a dump of the current state of your call stack whenever an exception is raised, and is helpful in finding out where things went wrong.

Although the first line of the stacktrace shows the line the exception ocurred on, it doesn’t always show the source of the error. In the example above, the program executed properly, but it couldn’t handle the data passed to the divide method. Walking up the stack trace leads to where it’s called, which is the source of the problem.

As always, we’d love to know how you liked this article, if you have any questions about it, and what you’d like to read about next, so be sure to let us know at @AppSignal.

10 latest articles

Go back

Subscribe to

Ruby Magic

Magicians never share their secrets. But we do. Sign up for our Ruby Magic email series and receive deep insights about garbage collection, memory allocation, concurrency and much more.

We'd like to set cookies, read why.