Skip to content
Java exceptions 6 min read

Nested try

A nested try block is simply a try block written inside another try block. This lets you handle different exceptions at different levels of granularity — catching some errors right where they happen while letting others bubble up to an outer handler.

Why Would You Use a Nested try?

Sometimes a block of code has several distinct steps, each of which can fail in a different way. Rather than lumping all possible exceptions into one giant catch, you can wrap the riskier sub-steps in their own try-catch, keeping your error-handling logic clear and focused.

Common real-world scenarios:

  • Opening a file, then parsing each line — a missing file is handled differently from a bad data format.
  • Establishing a database connection, then executing a query — a connection failure is fatal; a bad query might just skip that row.
  • Parsing user input, then doing arithmetic — a NumberFormatException is separate from an ArithmeticException.

Basic Syntax

try {                         // outer try
    // outer risky code
    try {                     // inner try
        // inner risky code
    } catch (InnerException e) {
        // handles inner exception only
    }
} catch (OuterException e) {
    // handles outer exception (and any uncaught inner one)
}

Both blocks can have their own catch and finally clauses. The nesting depth is unlimited, though deeply nested blocks quickly become hard to read.

Simple Example

public class NestedTryDemo {
    public static void main(String[] args) {

        int[] numbers = {10, 20, 30};

        try {
            System.out.println("Outer try: starting");

            try {
                // This will throw ArrayIndexOutOfBoundsException
                System.out.println(numbers[5]);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Inner catch: " + e.getMessage());
            }

            // Execution continues here after inner catch handled it
            int result = 10 / 0;   // ArithmeticException — not caught by inner
            System.out.println(result);

        } catch (ArithmeticException e) {
            System.out.println("Outer catch: " + e.getMessage());
        }

        System.out.println("After outer try block");
    }
}

Output:

Outer try: starting
Inner catch: Index 5 out of bounds for length 3
Outer catch: / by zero
After outer try block

The ArrayIndexOutOfBoundsException was caught and handled by the inner catch. The ArithmeticException was not caught in the inner block, so it propagated up and was caught by the outer catch. This is exception propagation in action.

How Exception Propagation Works in Nested try

When an exception is thrown inside an inner try:

  1. Java looks for a matching catch in the same (inner) try block first.
  2. If no match is found, the exception propagates to the outer try block’s catch clauses.
  3. If still unhandled, it continues propagating up the call stack until it either finds a handler or terminates the program.
public class PropagationDemo {
    public static void main(String[] args) {

        try {                                      // outer try
            try {                                  // inner try
                throw new RuntimeException("oops");
            } catch (NullPointerException e) {     // won't match
                System.out.println("Inner catch (not reached)");
            }
        } catch (RuntimeException e) {             // matches here
            System.out.println("Outer catch: " + e.getMessage());
        }
    }
}

Output:

Outer catch: oops

Note: finally blocks always run, whether or not an exception is thrown or caught. In nested try structures, each finally executes as its corresponding try block exits — inner finally first, outer finally second.

Nested try With finally

public class NestedFinally {
    public static void main(String[] args) {
        try {
            System.out.println("Outer try");
            try {
                System.out.println("Inner try");
                int x = 1 / 0;
            } finally {
                System.out.println("Inner finally");  // always runs
            }
        } catch (ArithmeticException e) {
            System.out.println("Outer catch: " + e.getMessage());
        } finally {
            System.out.println("Outer finally");  // always runs
        }
    }
}

Output:

Outer try
Inner try
Inner finally
Outer catch: / by zero
Outer finally

Notice the order: the inner finally runs before the exception reaches the outer catch, and the outer finally runs last. See finally Block for a deeper look at its guarantees.

Practical Example — File Read with Parsing

A realistic scenario where nested try shines: opening a file at the outer level and parsing each line at the inner level.

import java.io.*;

public class FileParseDemo {
    public static void main(String[] args) {
        try {                                             // outer: handles I/O errors
            BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
            String line;
            while ((line = reader.readLine()) != null) {
                try {                                     // inner: handles parse errors
                    int value = Integer.parseInt(line.trim());
                    System.out.println("Parsed: " + value);
                } catch (NumberFormatException e) {
                    System.out.println("Skipping bad line: \"" + line + "\"");
                }
            }
            reader.close();
        } catch (IOException e) {
            System.out.println("Cannot read file: " + e.getMessage());
        }
    }
}

Tip: In modern Java, prefer try-with-resources (try (BufferedReader r = ...)) for anything that implements AutoCloseable. It closes the resource automatically and keeps nesting shallower. You can still nest a plain try-catch inside a try-with-resources block.

Under the Hood

At the bytecode level, Java compiles try-catch blocks using an exception table stored in the .class file alongside each method’s bytecode. Each entry in that table says: “if an exception of type X is thrown between bytecode offset A and offset B, jump to handler at offset C.”

When you nest try blocks, the compiler generates multiple overlapping entries in the same exception table. The JVM scans this table top-to-bottom at runtime whenever an exception is thrown, so inner handlers (which appear earlier in the table) are checked before outer ones — mimicking the source-code nesting without any actual stack-frame overhead beyond the normal call stack.

The finally block is implemented by duplicating its bytecode into each possible exit path of the try block (normal return, each catch path, and uncaught path). This is why finally is guaranteed to run but also why you should avoid return or throw inside a finally — it can silently swallow exceptions from the try block.

You can inspect this yourself with the javap -c -verbose tool on any compiled class to see the exception table entries.

When to Use Nested try — and When Not To

SituationNested try?
Different steps that each throw distinct exception typesYes — keep handlers close to the cause
Inner loop inside a resource-opening blockYes — inner handles row/item errors, outer handles setup errors
All exceptions can be handled the same wayNo — a single try-catch or multiple catch is cleaner
More than two or three levels of nestingNo — refactor into smaller methods instead
Just checking whether something succeedsNo — consider returning an Optional or a result type

Warning: Deeply nested try blocks are a common code smell. If you find yourself writing try { try { try { ... } } } three levels deep, it is almost always a sign that the method is doing too much. Extract the inner logic into its own method with its own try-catch, then call it from the outer block.

Nested try vs Multiple catch

Both patterns deal with multiple exception types, but they serve different purposes:

  • Multiple catch blocks — several exceptions can be thrown at the same point in code. You just need different handlers for each type.
  • Nested try — different sections of the code throw different exceptions. You want handlers physically close to the section that might fail.

When both apply, prefer multiple catch blocks first; reach for nesting only when the separation of concerns genuinely requires it.

Last updated June 13, 2026
Was this helpful?