Let's start from the top, what is a stack trace? Even if you've never heard the term you've likely come across one. A stack trace is the name of the complicated chunk of text that comes up in your terminal, or logs, when you trigger an error in code. But why the name?
Understanding Code as a Stack
You're likely familiar with a stack as a data type in programming. It operates using LIFO (last in, first out). You "pop" something off the stack or "push" something onto it. As you continue to add to the stack, items are pushed further from the enter/exit point.
You may have heard of a postfix calculator that resolves using a stack? Code operates similarly and creates what is called a call stack. When you call a line of code, it pushes onto the stack. Methods that line of code calls are also pushed onto the stack. This happens all the way down the rabbit hole until it meets the very lowest building block of that line. It resolves that piece of code and starts working its way through the stack. Each time it pops something off the stack it resolves it and uses that result for the next thing on the stack. Eventually, it reaches that initial line of code and resolves it, leaving an empty stack.
Ok, So why does that matter?
Understanding code as a stack is crucial to making sense of a stack trace. Because a stack trace is showing you all of the different calls that were made while it attempted to resolve the piece of code you wrote. Somewhere in that stack of calls, an error was triggered and the system was unable to continue popping items off the stack without a result to use.
However, that means that the stack trace may not include the line you wrote that caused the error. Wait, what?!
Think about it this way. A piece of code you've written triggers an error at a lower level. If that level is too far down the stack trace, the message may cut off in a way that you can't tell the initial culprit.
How to read a stack trace
When you see this giant mess of text, what are you actually looking for? The reality is that each line of the stack trace is a piece of code you "called". However, you likely did so unknowingly, so it isn't particularly helpful.
What you're looking for is the first reference to a piece of code you wrote. It will often include a line number so you can more directly map to the error. Sometimes the stack trace is referencing only code you wrote, but that's not always the case.
Library code stack traces
The most complicated stack traces are often those in which the error was triggered in a third party library by code you wrote. In that case, you may be looking through a lot of lines before you find a reference to a file you recognize. Though the rest of the stack trace isn't pointing you to code that you can change, it's no less important.
Why? Well, it's telling you where the error ultimately came from. What part of your code broke a rule. Oftentimes the library files referenced are available for view in your IDE. If you dive down into them you might be able to figure out where what you wrote went wrong.
What about that catch business?
When you're writing code you're expected to take into account edge cases and defensively program. In a lot of cases this means catching exceptions and throwing a more specific exception as the cause.
public void getPost(int id) {
try {
post.getId(id); // this method throws a NullPointerException
} catch (NullPointerException e) {
throw new IllegalStateException("Post has a null property", e)
}
}
Code that does this affects the stack trace you'll see. It will give you a stack trace with "chained exceptions".
Exception in thread "main" java.lang.IllegalStateException: A post has a null property
at com.example.myproject.Author.getPost(Author.java:38)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
at com.example.myproject.Post.getId(Post.java:22)
at com.example.myproject.Author.getPost(Author.java:36)
... 1 more
Don't let this confuse you. You're still looking for the earliest reference to something you recognize.
If all else fails
Learning to read a stack trace is a practiced skill. If you want other tips for debugging I've written a post here.