6.1 Understanding Errors and Call Stacks
Prerequisites: Chapter 2 (Terminal Basics) and Chapter 5 (Package Management) Estimated time: 15 minutes Reading approach: First determine the failure phase, then extract evidence using the language's stack format You are ready to continue when: you can distinguish between compile-time errors and runtime exceptions, and identify the next investigative clue from a Java, Python, or Node.js stack trace
After a program fails, Git can tell you what changed and a debugger can pause a running process. Before changing code, read the evidence in the error output and call stack.
The Program Crashed
You add one line of code, compilation succeeds, and the next launch fills the screen with errors. Exception, Thread, NullPointer, and at are familiar words, but together they look like the log has volunteered for overtime. Do not guess yet: identify the phase that failed, then trace how execution reached that point.
First, Clarify Five Key Points
| Clue | Meaning | Does Not Mean |
|---|---|---|
| Compilation or parsing error | The source code failed to pass through a compiler or parser | Does not prove that the application ran |
| Exception | An abnormal condition occurred during execution | A caught exception does not necessarily terminate the process |
| Stack trace | A snapshot of the call path at the time of an exception | Does not automatically indicate the root cause |
stdout / stderr | Two output channels | The presence of content in stderr does not necessarily mean failure |
| Exit code | The status code indicating how a process terminated | The specific non-zero value is defined by the program itself |
Here, main.py stands for the program being diagnosed. Save the Python example later on this page before trying these commands. Check the exit code immediately after the command:
python3 main.py
echo "$?"To preserve evidence, record the status and save both output streams. This handles the expected failure even if the calling shell enables set -e:
status=0
python3 main.py >stdout.log 2>stderr.log || status=$?
printf 'status=%s\n' "$status"Terminal colors are not reliable indicators. Always verify the raw output, the exit code, and the program's documentation.
Java: Tracing Exceptions from the Stack Frame Toward the Caller
Exception in thread "main" java.lang.NullPointerException: <Diagnostic message varies by JDK>
at Main.processData(Main.java:8)
at Main.main(Main.java:4)This message is presented in sequence as follows:
- The thread that encountered the error is
main; - The exception type is
NullPointerException; - The message after the colon is a diagnostic provided by the JDK, whose exact wording may vary by version;
processDatarepresents the stack frame closest to where the exception was thrown;mainis the caller of that frame.
Save this as Main.java, compile it with the JDK's javac, and run it with java. The earlier trace illustrates the structure; use the paths and messages from your own output:
public class Main {
public static void main(String[] args) {
String input = null;
processData(input);
}
static void processData(String data) {
System.out.println(data.length());
}
}javac Main.java
java Main
echo "$?"Open the file and line number within the stack frame to inspect which values are used on that line, then trace back where those values originated. Adding a null check to data might prevent the crash, but it doesn’t necessarily explain why the value is null.
Caused by and Suppressed
java.lang.IllegalStateException: cannot load workshop configuration
at com.example.Boot.start(Boot.java:20)
Caused by: java.nio.file.NoSuchFileException: application.properties
at java.base/sun.nio.fs.UnixException.translateToIOException(...)The outer exception describes the current layer of abstraction, while Caused by: identifies an underlying exception. Continue through the exception chain, but also revisit the outer exception for business context.
Suppressed: typically indicates a secondary exception occurring during the handling of the primary exception, such as a failure to close a resource. It is not synonymous with Caused by:.
Python: Types and Messages Usually Appear at the End
def process_data(data):
return len(data)
process_data(None)Traceback (most recent call last):
File "main.py", line 4, in <module>
process_data(None)
File "main.py", line 2, in process_data
return len(data)
TypeError: object of type 'NoneType' has no len()Reading the traceback:
- Start with the final line containing
TypeErrorand the error message. - Move upward to find the most recent frame related to your code.
- Trace back through earlier frames to verify how the parameters were passed in.
- If the message says "During handling…" or "direct cause…", read both the preceding and following error messages together.
Syntax errors occur before execution and often include a caret symbol indicating where the parser encountered a problem. That position may simply be where parsing stopped, sometimes the missing parenthesis or quote is actually on the previous line.
Node.js: Messages Come First, Stack Frames Follow
function processData(data) {
return data.length;
}
processData(null);TypeError: Cannot read properties of null (reading 'length')
at processData (/app/main.js:2:15)
at Object.<anonymous> (/app/main.js:5:1)Save the JavaScript as main.js and run node main.js to reproduce it. The output above shows the relevant frames; paths depend on the working directory. The numbers after the filename are usually line:column. If you're running a transpiled or bundled output, verify that source maps and the deployed version match; otherwise, the line numbers may not accurately correspond to the original source code.
Don't Mechanically Hunt for "Your First Line of Code"
When dealing with many third-party library frames, prioritize frames that are directly related to your inputs, configurations, and calls, don’t remove all library frames at once:
- Library-generated error messages may precisely identify invalid parameters;
- When a framework wraps an exception, the root cause might lie deeper in the
Caused by:; - Asynchronous queues, thread pools, and remote services can break the local call stack;
- If source code from the same module has inconsistent versions, line numbers can mislead.
A more reliable four-question approach is:
- What is the most specific exception type and message?
- Which stack frame is closest to the failed operation?
- Which upstream frame provided invalid input or an erroneous state?
- Is there an exception chain, request ID, or nearby logs that provide context?
Quick Reference
| What you see | Next step |
|---|---|
| The compiler reports dozens of errors in a row | Fix the earliest one first, then recompile |
Java Caused by: | Expand the inner cause, then revisit the outer semantics |
| Python traceback | Read the last line first, then locate the most recent relevant frame |
Node file:line:column | Verify the actual runtime file against the source map |
| Only "process exited" | Save stdout, stderr, and the exit code |
| Line numbers don't match source | Cross-check the build output, version, and debug symbols |
Before You Leave This Page
- Run the Java or Python examples and identify the exception type, error message, failing frame, and calling context.
- Explain why compilation success does not guarantee runtime success.
- Clarify that there is no one-to-one correspondence between
stderr, exceptions, and non-zero exit codes. - When encountering an exception chain, do not discard
Caused by:or Python’s chained exceptions.
You now recognize stages, exceptions, and call paths from red-highlighted output. But when no one is watching the terminal at night, the runtime state must still be preserved.