Throwing and Catching Standard Exceptions
A bare throw; inside a catch block rethrows the caught exception. The function does not return normally after the handler finishes, because the exception continues to propagate.
Try it: try / except / else / finally
How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.
How it works
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- finally runs in every case.
Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.
Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.
Loading the simulation…
Predicting Which Handler Runs
A single try block can be followed by several catch blocks, and each one names the type of exception it accepts. When something goes wrong inside the try block, the program does not simply stop or pick a handler at random. The type of the exception that was raised decides which handler receives it. Before reading the explanation, make a prediction about the situation below.
What do you think happens?
A try block has two handlers, one for std::length_error and one for std::bad_alloc. The operation inside the try block throws a std::length_error. Which handler runs, and what happens to the other handler?
Reveal answer
The std::length_error handler runs, and the std::bad_alloc handler is skipped.
Each catch names the type it accepts. The thrown exception matches the std::length_error handler, so that handler receives it. The other handler does not match and is passed over.
The rest of this article traces that decision step by step. The skill it builds is the ability to say, for any failure, which handler matched and what happened next. That is the skill you need to locate the point where a program's behavior diverged from what you expected.
Matching a Thrown Exception to Its Handler
When the try block runs to completion without raising an exception, no handler runs at all. When an exception is raised, the program looks at the exception's type and selects the catch block whose declared type matches. The matching handler runs, and the other handlers attached to that try block are skipped. This is how one function can respond differently to different kinds of failure instead of treating every failure the same way.
Read the diagram from top to bottom. The first question is whether anything was raised. If nothing was, the flow ends at the completed try block. If something was raised, the type is checked against each handler in turn. A match sends control into that handler. A handler that does not match is never entered for that exception. The source describes the two handlers in the example as separate cases: one labeled as a negative size, and one labeled as memory exhaustion. Each catch names a different type, so the handler that runs depends entirely on which type was raised.
Two Handlers in One Function
The code below is an illustrative sketch of the function the source describes. The source discusses the structure of the try and catch blocks and the logic of the handlers, but it does not give the full code text in this pack, so the body of the try block is a stand-in for whatever operation might fail. Focus on how the handlers are named by the exception types they accept.
- The try block holds the operation that may fail
- The first catch accepts std::
length_error, the case labeled as a negative size - The bare throw; rethrows the caught exception
- The second catch accepts std::
bad_alloc, the case labeled as memory exhaustion - std::
terminate()ends the program in that second handler
Tracing a std::length_error through test()
Assume the exception raised inside the try block is std::length_error, the case the source labels as a negative size. Trace which handler runs and what happens next.
- 1Step 1: the exception is raised
Control leaves the normal sequence of the try block at the point of the throw, and the rest of the try block is skipped.
- 2Step 2: the handlers are checked in order
The first catch names std::
length_error, which matches the raised type, so that handler is selected. - 3Step 3: the handler body runs
The message is printed. The bare throw; then rethrows the same exception instead of finishing normally.
- 4Step 4: the second handler is skipped
The std::
bad_allochandler does not match a std::length_error, so it never runs for this exception.
The length_error handler runs, prints its message, and rethrows. The exception then leaves test() and continues to whatever code outside this function handles it. That outer code is not shown in the source.
Carrying Information in a Custom Exception
A custom exception class is a class you define yourself and then throw as an exception. Its purpose is to carry arbitrary information from the place where an error is detected to the place where that error can be handled. The class is not merely a label for the error. It is a container for whatever details the handler will need.
A standard exception type such as std::length_error tells a handler only what category of problem occurred. A custom class can describe the situation in more detail. The source states this general idea but does not show a full class definition, so this article does not present one. What to take away is the role: the exception object is a vehicle for information, and its class determines what that information can look like. The exception is created at the throw site, travels outward through the calls, and the handler at the catch site reads its contents.
Rethrowing with a Bare throw
Sometimes a handler does useful work and then decides that the error should continue upward rather than being resolved in the current function. The source calls this rethrowing. The statement throw; has no operand. Placed inside a catch block, it rethrows the exception that was caught, so the error propagates further. The handler does not stop the error. It performs its own work and then passes the same error onward.
The diagram captures the key change. Control does not finish normally after the catch block. The exception leaves the handler and continues on its way. Whether anything further up handles it depends on code outside this function, which the source does not show. This is why a rethrow is a decision to pass responsibility onward, not a way of making the error disappear.
A bare throw; inside a catch block rethrows the caught exception. The function does not return normally after the handler finishes, because the exception continues to propagate.
Calling std::terminate() Deliberately
Not every exception should be passed onward or recovered from in the current function. In the second handler of the rethrowing example, the code catches std::bad_alloc and calls std::terminate(). The source's reasoning is that test() is not designed to handle memory exhaustion. The function has no sensible way to continue, so the code ends the program on purpose instead of pretending the error was handled.
Where the Control Path Diverges
The normal path through a function runs each statement in order and reaches the end. An exception changes that path at the moment it is raised. Statements after the throw point inside the try block are skipped, and control jumps to a handler chosen by type. The table below compares what you might expect with what actually happens in the test() example.
| Situation | Expected path | Actual path with an exception |
|---|---|---|
| Statements after the throw inside try | Run in order | Skipped; control leaves the try block |
| Which handler is considered | The first handler listed | Only the handler whose type matches the exception |
| After the length_error handler | Function finishes normally | Bare throw; rethrows and the exception propagates outward |
| After the bad_alloc handler | Function finishes normally | std::terminate() ends the program |
Expected versus actual control flow in test()
Mistakes Beginners Make with Handlers
Common mistake Assuming every catch block runs when an exception is raised
Why it failsThe type of the exception decides which handler receives it. Handlers whose type does not match are skipped.
Do this insteadFor any thrown exception, identify the one catch whose declared type matches, and trace only that handler.
Common mistake Writing throw; outside a catch block and expecting it to rethrow something
Why it failsThe bare throw; rethrows the exception that was caught. Without a handler in progress there is no caught exception to pass on.
Do this insteadUse the bare throw; only inside a catch block, where it rethrows the exception that handler received.
Common mistake Believing a rethrow means the error has been handled
Why it failsA rethrow passes the same exception onward. Whatever handles it next is code outside the current function.
Do this insteadAfter a rethrow, follow the exception to the next handler up the calls and check which one matches.
Common mistake Using std::terminate() as a shortcut to avoid writing a real handler
Practice: Tracing Two Failures
Using the test() function from this article, trace two cases. In the first, the exception raised is std::length_error. In the second, it is std::bad_alloc. For each case, name the handler that matched, state whether the bare throw; or std::terminate() was reached, and say what happens to the program next.
Hints
- Check the type of the exception against each catch in order, the same way the flowchart does.
- Only the matching handler runs. The other handler is skipped.
- A bare throw; means the exception keeps propagating outward. std::
terminate()ends the program.
Key Takeaways
- A try block can have several catch blocks, and the exception's type decides which one runs; the other handlers are skipped.
- A custom exception class carries arbitrary information from the point where an error is detected to the point where it is handled.
- A bare throw; inside a catch block rethrows the caught exception so that it propagates further.
- std::
terminate()ends the program when a function is not designed to handle an exception, and it should be used deliberately.