A catalog of common runtime errors
On This Lesson 11 topics
Lesson Overview
By now you have seen several crashes: an index too far, a null, a bad number. Most beginner crashes come from a short list of causes. A detective who knows the usual suspects does not start from zero: the name of the exception already narrows the case.
This lesson is a field guide. For each common runtime error you learn what the name means, what usually causes it, and the first thing to check. It is meant to be a reference you come back to, and it applies the stack-trace reading from the previous lesson.
Why this matters: in a backend job, the exception name is often the first clue you get, in a log line or a ticket title. Recognizing it quickly saves time and prevents random changes.
This lesson builds on arrays (BEG-015), Strings (BEG-015-L04), null (BEG-009), Scanner (BEG-011), recursion (BEG-014-L06), and reading stack traces (previous lesson). The try/catch form is not taught yet; we only look at what the crashes print.
Learning Objectives
By the end of this lesson you will be able to:
- Name the exception for each of: null use, bad array index, bad string index, integer division by zero, bad number text, endless recursion, and wrong
Scannerinput. - Give the typical cause of each and the first thing to check.
- Distinguish
ArithmeticExceptionfrom the silent results of floating-point division by zero. - Say why
StackOverflowErroris an error and not an exception, in one sentence. - Match a short piece of code to the exception it will cause.
- Use the catalog as a checklist in the hypothesis step of the debugging process.
Written Explanation
The catalog
| Name | Typical cause | First thing to check |
|---|---|---|
NullPointerException | Using a reference that points to nothing (null), e.g. calling a method on it | Which variable does the message name? Who set it to null or never set it? |
ArrayIndexOutOfBoundsException | Index below 0 or not less than the array's length | The loop condition (< versus <=) and the index expression |
StringIndexOutOfBoundsException | Bad index in charAt, substring and similar | The string's length; is the text shorter than you assumed? |
ArithmeticException | Integer division (or %) by zero | Where does the divisor come from? Can it be 0? |
NumberFormatException | Text that is not a valid number given to Integer.parseInt and similar | What is the exact text? Spaces, letters, or an empty string? |
StackOverflowError | Recursion with no stopping case, or one never reached | The base case, and whether each call moves towards it |
InputMismatchException | Scanner.nextInt() (and similar) met text of the wrong kind | Check with hasNextInt() before reading |
All but one of these are exceptions in the family the Java documentation describes as unchecked (they extend RuntimeException), which means the compiler does not force you to deal with them. StackOverflowError is an Error: the documentation puts it under VirtualMachineError, for serious problems an application is not usually expected to recover from. For you, the practical difference is that it means "the recursion is broken", not "the data is bad".
NullPointerException
The documentation lists the cases: calling an instance method on null, accessing a field of null, taking the length of null as if it were an array, and similar. Remember from BEG-009 that a reference variable holds either an object or null. The crash happens when you use the variable, but the mistake happened earlier, when it was set to null or never given a value. Read the message (for example because "s" is null) and trace the origin backward.
ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException
An array of length 5 has indexes 0 to 4. The documentation says the exception is thrown when the index is negative or not less than the length. The message contains both numbers. For strings the rule is similar: "hello".charAt(5) fails because the valid indexes are 0 to 4, the same as an array. A substring call with an end beyond the length fails too, and the exact wording of such messages varies between Java versions, so read for the numbers and not the phrasing.
The usual causes are an off-by-one loop bound (BEG-013-L06), forgetting that indexing starts at 0, and assuming that input is longer than it is.
ArithmeticException
In Java, 10 / 0 with integers throws ArithmeticException with the message / by zero, and so does 10 % 0. Floating-point arithmetic behaves differently: 10.0 / 0 does not throw, it gives Infinity (and 0.0 / 0 gives NaN, "not a number"). That difference is a trap: an integer bug is loud and a double bug is silent. In our run, 10.0 / 0 printed Infinity.
NumberFormatException
Raised when text is turned into a number and the text is not a valid number. Integer.parseInt("abc") fails, and so do "1O" (letter O), "" (empty), and "3.5" (not an integer). In our run, an empty string produced For input string: "", and null produced Cannot parse null string. Note that parseInt(" 12 ") with spaces also fails, since it does not trim; trim() first (from BEG-015) if spaces are possible. The first thing to check is the exact text, which the message shows in quotes.
StackOverflowError
Caused by runaway recursion: each call adds a frame to the call stack (BEG-014-L05) and the stack is limited in size. The trace is one line repeated. Check the base case: is it reached? Does each call move the argument towards it? A method that calls itself with n + 1 when the base case tests n == 0 will never stop.
InputMismatchException
Scanner's nextInt() expects the next token to look like an integer. The documentation says it throws InputMismatchException when the token does not match the integer pattern or is out of range. In our test, typing abc at a nextInt() crashed the program with only the exception name, no message. The safer pattern you saw in BEG-011 and BEG-013-L07 is to ask first with hasNextInt(). If nothing is left to read at all, Scanner throws NoSuchElementException, the superclass of InputMismatchException.
Using the catalog
In the hypothesis step of Lesson 1, start from the table:
- Name the exception and read the message.
- Find the row and its typical cause.
- Check the "first thing" in the debugger, with a watch or Evaluate Expression.
- If the evidence rejects the theory, you have learned something. Continue from the trace.
The catalog gives you a first hypothesis, not a diagnosis. It cannot tell you why a variable was null; the chain of callers does.
Examples
Example 1: match the code to the error (simple)
Each line is run in its own small program. These are the results from a real JDK 21 run, with the exception name and message:
| Code | Result |
|---|---|
String s = null; s.length(); | NullPointerException: Cannot invoke "String.length()" because "s" is null |
int[] a = new int[5]; a[5] = 1; | ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 |
"hello".charAt(5) | StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 |
int z = 0; 10 / z | ArithmeticException: / by zero |
Integer.parseInt("abc") | NumberFormatException: For input string: "abc" |
a[-1] = 1; (array of 5) | ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 5 |
Notice that a negative index is also "out of bounds", and that the string message here reused the array wording. The type narrows the case; the message gives the numbers.
Example 2: wrong input to Scanner (practical)
Scanner in = new Scanner(System.in);
System.out.print("Quantity: ");
int q = in.nextInt();
System.out.println("You ordered " + q);With abc typed at the prompt, the run printed:
Quantity: Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:947)
at java.base/java.util.Scanner.next(Scanner.java:1602)
at java.base/java.util.Scanner.nextInt(Scanner.java:2267)
at java.base/java.util.Scanner.nextInt(Scanner.java:2221)
at Menu2.main(Menu2.java:7)The first line of your code is Menu2.java:7, the nextInt() call. The cause is the data typed by the user. The program cannot stop users from typing letters, so the fix is to ask first:
if (in.hasNextInt()) {
int q = in.nextInt();
System.out.println("You ordered " + q);
} else {
System.out.println("Not a number: " + in.next());
}With the same abc input this printed Not a number: abc. Running the program with typed input in the IDE console works the same way as in a terminal.
Example 3: an empty list reveals the integer/double difference (realistic)
static int averageInt(int[] v) {
int sum = 0;
for (int x : v) { sum += x; }
return sum / v.length;
}
static double averageDouble(int[] v) {
int sum = 0;
for (int x : v) { sum += x; }
return (double) sum / v.length;
}
// main: both called with an empty arrayFor an empty array, averageDouble printed NaN, and averageInt crashed:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Empty.averageInt(Empty.java:5)
at Empty.main(Empty.java:15)Both versions have the same root problem: the average of nothing is undefined, and the code did not consider an empty list. One crashes loudly, the other quietly returns a value (NaN, which spreads through later calculations). The loud one is easier to debug. For the quiet one you need the edge-case habit from BEG-016-L05: decide what an empty list should do, and handle it before dividing.
Backend Insight
In backend services, the same few exceptions cause a large share of failures: null fields in objects read from a database, numbers parsed from request text, list indexes taken from user input. Because the names are standard, teams often search logs by exception name to see how often a problem happens. One report per NumberFormatException usually means users send bad data, while NullPointerException in the same place usually means missing data or an unhandled case in your own code.
Good services validate input at the edge and fail with a clear message instead of letting a raw exception escape. You will learn how in a later module. The catalog tells you where such validation matters most.
Common Mistakes
Mistake 1: Memorizing names but not causes
The name tells you the kind of problem. The trace and the data tell you where it came from.
Mistake 2: Guarding the symptom
Wrapping a crash so that it disappears (or checking i < 5 when the real array length is a variable) hides the cause. Find out why the value was bad.
Mistake 3: Trusting a silent NaN or Infinity
Double arithmetic does not crash on division by zero. Check results that might propagate.
Mistake 4: Parsing text without looking at it
parseInt fails on spaces, letters, decimals, and empty text. Look at the exact string in the message.
Mistake 5: Treating StackOverflowError as a memory problem
It is about call depth. Look at the recursion, not the size of your data.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Name the exception for each: (a) Integer.parseInt("4.5"); (b) String t = null; t.trim();; (c) int[] a = {1, 2, 3}; a[3].
Show solution A1
A1. (a) NumberFormatException: "4.5" is not an integer. (b) NullPointerException: t is null. (c) ArrayIndexOutOfBoundsException: valid indexes are 0 to 2.
A2. What is the first thing to check for ArithmeticException: / by zero?
Show solution A2
A2. Where the divisor comes from, and whether it can be 0 for the failing input. Inspect it in the debugger.
A3. Does 5.0 / 0 throw an exception? What does it give?
Show solution A3
A3. No. Floating-point division by zero gives Infinity.
Level B: Application
B1. A method returns text.charAt(text.length()). Name the exception and explain why, with the valid indexes for the text "java".
Show solution B1
B1. StringIndexOutOfBoundsException. The length of "java" is 4, so the valid indexes are 0 to 3, and index 4 is one past the end. (The last character is charAt(text.length() - 1).)
B2. A loop for (int i = 0; i <= scores.length; i++) total += scores[i]; fails at the end. Which exception, what is the message for a length-3 array, and what is the fix?
Show solution B2
B2. ArrayIndexOutOfBoundsException, message Index 3 out of bounds for length 3. The condition i <= scores.length lets i reach 3. Fix: i < scores.length.
B3. A menu reads int choice = in.nextInt(); and the user presses q. Name the exception, and rewrite the read so that the program prints Please type a number instead of crashing.
Show solution B3
B3. InputMismatchException. Rewrite:
if (in.hasNextInt()) {
int choice = in.nextInt();
// use choice
} else {
System.out.println("Please type a number");
in.next(); // discard the bad token
}Level C: Challenge
C1. Write a method percent(int part, int whole) returning part * 100 / whole. Show an input that causes ArithmeticException, and write a version that reports "no data" with the result -1 when whole is 0.
Show solution C1
C1. percent(5, 0) throws ArithmeticException: / by zero. Version:
static int percent(int part, int whole) {
if (whole == 0) {
return -1; // no data
}
return part * 100 / whole;
}C2. A recursive method countdown(int n) prints n and calls countdown(n - 1) when n != 0. Which call crashes it, and why? Fix it in a way that also handles negative input.
Show solution C2
C2. countdown(-1) (or any negative start): n goes -1, -2, and so on and never equals 0, so recursion never stops and a StackOverflowError follows. Fix: stop when n <= 0 (or reject negatives), for example if (n < 0) return; before the recursion, or change the test to n > 0.
Mini Challenge
Make your own catalog card. Pick three exceptions from the table and, for each, write a three-line program that triggers it, run it, and copy the first line of the trace. Then add a fourth row of your own: a crash you meet during the week, with its cause and the first thing you checked.
Check your work: each row has an exception name, a cause in your own words, and a check that you could do in the debugger.
Summary & Next Lesson
Key takeaways
- Seven common names cover most beginner crashes:
NullPointerException, the two index exceptions,ArithmeticException,NumberFormatException,StackOverflowError, andInputMismatchException. - Each has a typical cause and a first thing to check; the message often gives the exact values.
- Integer division by zero throws; floating-point division by zero gives
InfinityorNaNsilently. StackOverflowErrorpoints at a recursion that does not stop.- The catalog gives you a first hypothesis, not the answer.
Next lesson
You now have the whole toolbox: a method, a debugger, watches, stack traces, and a catalog. The next lesson, "Demo: a full bug hunt from report to fix", puts all of it into a single case, and prepares you for the lab.
Self-check
- Which exception comes from
Integer.parseInt("abc")? - What does
Index 5 out of bounds for length 5tell you about the valid indexes? - What does
10.0 / 0give? - How do you avoid
InputMismatchExceptionfromnextInt()? - What does a repeated trace line point to?
Show answers
NumberFormatException.- They are 0 to 4.
Infinity, with no exception.- Check
hasNextInt()before callingnextInt(). - Recursion that does not stop, a
StackOverflowError.
References
- Oracle Java SE 21 API: NullPointerException (cases in which it is thrown), ArrayIndexOutOfBoundsException (index negative or not less than length), StringIndexOutOfBoundsException (thrown by
Stringmethods for bad indexes; message format unspecified). - Oracle Java SE 21 API: ArithmeticException (integer division by zero), NumberFormatException (string not in the right format, for example with
Integer.parseInt), InputMismatchException (token does not match the requested type, thrown byScanner), StackOverflowError (anErrorthrown when an application recurses too deeply). - The behavior of
10.0 / 0(Infinity), theNaNresult, theparseIntmessages for empty andnullinput, and all the quoted messages come from compiling and running the code with JDK 21. The statement that floating-point division by zero does not throw also matches the Java documentation forArithmeticException, as read; I did not look up the Language Specification section on floating-point division directly.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.