First exams 2027 · SL & HL

B2.1.3 · Programming fundamentals

Exception handling: describing how programs deal with failure

Every program meets input it didn't expect, a resource that isn't there, or its own logic breaking down for one particular case. This statement is about the constructs that let a program describe what to do when that happens, instead of just crashing.

Core for SL & HL Typically 2–4 marks · AO1/AO2 Command term: describe

Explainer

Volunteers at the Bramble Hill Fun Run type each finisher's time into a laptop as runners cross the line. Most entries are fine. Occasionally a volunteer fumbles the keyboard and types a letter instead of a digit. The laptop has no results file yet, since this is the first run of the season. And once in a while, nobody has finished a full kilometre yet when the program tries to work out an average pace. None of these should crash the whole kiosk and lose every time already logged. That is exactly the problem exception handling exists to solve.

Df

An exception is an event triggered when something goes wrong while a program is running, for example dividing by zero or converting text that isn't a number. Left alone, it stops the program immediately at that line, and everything after it never runs.

Df

Exception handling is code that names specific things that could go wrong ahead of time and states what the program should do instead, if one of them actually happens. The program anticipates the failure rather than being surprised by it.

The role exception handling plays in a program

Without it, one bad entry anywhere in a run of the program takes the whole program down with it, and every result already computed before that point can be lost. With it, the program can skip just the bad case and carry on, or fail that one operation gracefully with a clear message, while everything else keeps working. It also keeps the error-handling decisions in one clearly marked place, separate from the main logic, rather than scattered through it as constant if-checks.

Explainer

Three ways a program meets failure

The syllabus names three categories, and the kiosk hits all three in a single run.

1

Unexpected input. Data doesn't match what the program was written to expect, a volunteer types "abc" where a whole number of seconds was required. The code itself has no bug, the DATA handed to it does.

2

Resource unavailability. Something the program needs isn't actually there when it runs, a file, a network connection, a device. The kiosk tries to open a results log from a previous run, and this being the season's first run, that file has never been created.

3

Logic errors. The code's own arithmetic breaks down for one particular case, even though it works correctly in general. Dividing total time by the number of finishers works fine, until zero runners have finished and that division has no sensible answer.

Explainer

try, except and finally, in Python and Java

Both languages structure exception handling the same way: attempt some code, name what to do for each specific kind of failure, and optionally guarantee one block of code runs regardless of what happened.

ConstructWhat it doesPythonJava
tryMarks the code that might failtry:try {
except / catchNames ONE specific kind of failure and what to do about itexcept ValueError:catch (NumberFormatException e) {
Multiple handlersDifferent failures get different responsesRepeat except for each kindRepeat catch for each kind
Catch anythingOne handler for every possible failure, without naming which one occurredexcept:catch (Exception e) {
finallyRuns no matter what happened above itfinally:finally {

A bare except: is valid, simpler to write, and needs no research into exception names, real code uses it too. The trade-off is diagnostic: it cannot tell you or the user which specific thing went wrong, only that something did.

"finally" is not "the code after try"

A line written directly after a try/except block only runs if nothing above it stopped the program, an uncaught exception skips straight past it. Code inside finally is different: it runs whether the try block succeeded, whether an exception was caught, and even if an exception was never caught at all and is about to crash the program regardless. It is the one guarantee in the whole construct.

Worked example

The finish-line kiosk, guarding against all three failures

Three separate try blocks, one per failure category, each naming the exact exception it expects rather than catching everything indiscriminately.

Handling unexpected input, a missing resource, and a logic error Step 1 of 6

Step 1. Three raw entries arrive. One volunteer mistyped a time as text. Nothing has been processed yet.

finish_times_raw = ["1518", "abc", "1402"] total_seconds = 0 completed_km = 0

Step 2. Unexpected input: int() fails on "abc". try wraps only the conversion and the running totals it feeds. except ValueError catches exactly that failure, prints a message, and the loop moves straight on to the next entry, nothing crashes.

for entry in finish_times_raw: try: seconds = int(entry) total_seconds = total_seconds + seconds completed_km = completed_km + 5 print("Logged:", seconds, "seconds") except ValueError: print("Entry", entry, "is not a whole number, skipped it")
Output so far
Logged: 1518 seconds
Entry abc is not a whole number, skipped it
Logged: 1402 seconds
total_seconds = 2920, completed_km = 10. The bad entry cost nothing but itself.

Step 3. Resource unavailability: no previous log exists yet. open() in read mode requires the file to already exist. except FileNotFoundError catches that specific failure. finally then runs regardless, printing its message whether the file opened or not.

try: previous_log = open("results.txt", "r") print("Found a previous log") previous_log.close() except FileNotFoundError: print("No previous log yet, starting fresh") finally: print("Finished checking for a previous log")
Output so far
No previous log yet, starting fresh
Finished checking for a previous log
The finally line runs even though the try block failed. It would run identically if the file HAD opened successfully.

Step 4. Logic error, guarded but not triggered this run. completed_km is 10, so this division succeeds normally. The except exists for a case that isn't happening today, defensive code that costs nothing when it isn't needed.

try: average_pace = total_seconds / completed_km print("Average seconds per km:", round(average_pace, 1)) except ZeroDivisionError: print("No runners finished a full km yet, cannot calculate a pace")
Output so far
Average seconds per km: 292.0

Step 5. The same code, a slower morning. If every entry typed so far had been invalid, completed_km would still be 0 when this line runs, and the division genuinely fails. This time except ZeroDivisionError is the branch that actually executes.

finish_times_raw = ["abc", "xyz", "???"] # ... same code as steps 2 to 4 ...
Output this time
Entry abc is not a whole number, skipped it
Entry xyz is not a whole number, skipped it
Entry ??? is not a whole number, skipped it
No runners finished a full km yet, cannot calculate a pace
completed_km stayed 0, so this time the except branch is the one that runs, not the print above it.

Step 6. Recap: one construct per category. Three separate, narrowly-named handlers, each matched to exactly the failure it anticipates, kept the whole kiosk running through every case above.

What guarded what
Unexpected input → except ValueError
Resource unavailability → except FileNotFoundError, plus finally
Logic error → except ZeroDivisionError

Try it yourself

Run the finish-line kiosk

The same program as the steps above, live and editable. Two experiments are waiting at the bottom: make every entry invalid and watch the logic-error branch actually fire this time, then try an exception with no matching except and see finally still run before the program stops.

main.py
Loading Python engine…
Output
Press Run to see the output here.

Running in offline fallback mode. The full Python engine couldn't load (blocked school network, or no internet), so this console switched automatically to a small hand-written engine. It supports try/except/finally, raise, and the exact exceptions this page needs (ValueError, ZeroDivisionError, FileNotFoundError), but it isn't full Python. It also has no real filesystem, so open() always behaves as if the file genuinely doesn't exist, which is exactly the case this page wants to demonstrate. One visible difference: whole-number results print as 292 here rather than Python's 292.0.

Check your understanding

A program has try: risky_step() except ValueError: print("caught") then finally: print("cleanup"). risky_step() raises a TypeError, not a ValueError. What happens?

Common pitfall

A well-known misconception about B2.1.3-style exception handling

RecurringThe most common misdescription of this statement is finally itself. It is frequently described as "the code that runs if the try block succeeds" or "runs when there's no exception", when it is actually the opposite kind of guarantee: it runs in every case, success, a caught exception, or even an exception that was never caught at all and is about to stop the program regardless.

The second common gap is not naming an exception type at all. A bare except: is valid syntax, catches anything, and needs nothing extra, but it never tells you or the user WHICH failure actually happened. Naming the specific exception, ValueError or ZeroDivisionError rather than a bare except:, is what lets the program (or the programmer debugging it) actually respond to what went wrong, not just that something did.

Answer frame that scores: name the specific exception type for each anticipated failure → state that finally runs unconditionally, not just on success → explain that an unmatched exception still propagates and stops the program after finally has run.

✕

What the misconception looks like: describing finally as "the block that runs once the try has finished successfully" or "confirms the operation worked". That description is simply wrong, finally runs after a caught failure too, and even ahead of an uncaught one crashing the program.

✓

What it should look like: "finally always executes, regardless of whether the try block succeeded or an exception was raised and caught, or even if it was raised and never caught at all." Unconditional, stated as unconditional.

Revision questions

Past-paper style. Write an answer, then reveal the marking points and mark yourself.

Question 1 · 2 marks

[2]Describe the purpose of the finally block in exception handling.

Question 2 · 3 marks

[3]Identify the three categories of potential failure a program should anticipate, giving one example of each.

Question 3 · 4 marks

[4]A program reads a whole number from a file that might not exist. Describe how try, except and finally would each be used to handle this safely.

Flashcards

Active recall for this statement’s key terms.