Lesson 10 of 10
Lesson 10

Exception Handling

Write programs that respond gracefully to runtime errors instead of crashing. Understand the three types of programming error and how try/except/finally protects your code.

35 min GCSE Level Errors, Try/Except, Robustness
Language:

Imagine an ATM crashing with a cryptic error message because someone pressed Cancel at the wrong moment - or a hospital system going down because a nurse typed letters into a numeric field. Exception handling is what separates reliable software from fragile software.

Think about it: What should a program do when the user types "abc" where a number is expected? List three sensible responses and decide which is most user-friendly.
Exception
An error that occurs while the program is running, disrupting the normal flow of execution.
try block
The section of code that might raise an exception - the program attempts to run it.
except / catch block
Code that runs only if the try block raises an exception - handles the error gracefully.
finally block
Code that always runs whether or not an exception was raised - used for clean-up.
Raising an exception
Deliberately triggering an exception with raise / throw to signal that something has gone wrong.
Robust program
A program that handles unexpected input or situations without crashing.

Three Types of Programming Error

You must be able to identify and distinguish between syntax, runtime and logic errors in the exam.

Syntax Error
Breaks the rules of the language - detected by the interpreter/compiler before the program runs.

Example: pint("hello") - misspelled keyword.

The program will not run at all.
Runtime Error (Exception)
Occurs while the program is running - it was syntactically correct but something went wrong during execution.

Example: dividing by zero, accessing an index that does not exist.

This is what exception handling catches.
Logic Error
The program runs without crashing but produces incorrect results because the algorithm is wrong.

Example: calculating average by dividing by n+1 instead of n.

Cannot be caught by exception handling - requires testing and debugging.

try / except / finally

Wrap potentially dangerous code in a try block. If an exception is raised, control jumps to the matching except (or catch) block. The finally block always runs regardless.

try:
    number = int(input("Enter a number: "))
    result = 100 / number
    print(f"Result: {result}")

except ValueError:
    print("Error: please enter a whole number.")

except ZeroDivisionError:
    print("Error: cannot divide by zero.")

finally:
    print("Calculation complete.")  # always runs
try
{
    Console.Write("Enter a number: ");
    int number = int.Parse(Console.ReadLine());
    double result = 100.0 / number;
    Console.WriteLine($"Result: {result}");
}
catch (FormatException)
{
    Console.WriteLine("Error: please enter a whole number.");
}
catch (DivideByZeroException)
{
    Console.WriteLine("Error: cannot divide by zero.");
}
finally
{
    Console.WriteLine("Calculation complete.");  // always runs
}
Order matters

Catch more specific exceptions before more general ones. In Python, a bare except: catches everything - useful as a last resort but makes debugging harder because you cannot tell what went wrong. Always prefer named exception types.

Raising Exceptions Deliberately

You can raise an exception yourself when data is invalid, rather than letting the program continue with bad values. This makes bugs easier to find and fixes easier to make.

def set_age(age):
    if age < 0 or age > 130:
        raise ValueError(f"Age {age} is not valid.")
    return age

try:
    set_age(-5)
except ValueError as e:
    print(e)  # Age -5 is not valid.
static int SetAge(int age)
{
    if (age < 0 || age > 130)
        throw new ArgumentOutOfRangeException(
            "age", $"Age {age} is not valid.");
    return age;
}

try { SetAge(-5); }
catch (ArgumentOutOfRangeException e)
    { Console.WriteLine(e.Message); }

Error Trigger Sandbox

Select a scenario to see how each exception type behaves - with and without a try/except block.

Choose a scenario
Code (with exception handling)
Output

Three Quick Challenges

Predict the output

Trace through the try/except/finally carefully. What two lines are printed?

try:
    print(1 / 0)
except ZeroDivisionError:
    print("caught")
finally:
    print("done")
try { Console.WriteLine(1 / 0); }
catch (DivideByZeroException)
  { Console.WriteLine("caught"); }
finally
  { Console.WriteLine("done"); }
Fill in the blank

Complete the exception handler to catch a ValueError:

try: x = int("abc") ValueError: print("Not a number")
try { x = int.Parse("abc"); } (FormatException) { Console.WriteLine("Not a number"); }
Spot the bug

This code swallows an error silently. What is wrong with it?

try:
    result = int("abc")
except:
    pass
print(result)

Check Your Understanding

1. A student writes pint("hello"). What type of error is this?
pint is not a valid Python keyword - this is a misspelling caught before the program runs, so it is a syntax error.
2. Which block of code is guaranteed to run whether an exception is raised or not?
The finally block always executes. It is commonly used to close files or database connections so resources are never left open.
3. A program calculates the average of a list but divides by len(numbers) + 1 instead of len(numbers). What type of error is this?
The code is syntactically valid and will not crash - but it produces the wrong answer. A wrong algorithm is a logic error.
4. What Python exception is raised when you call int("hello")?
ValueError is raised when a function receives the right type of argument but an inappropriate value - "hello" is a string (right type for conversion) but cannot be parsed as an integer.
5. Why should you prefer catching specific exceptions (e.g. except ValueError) over a bare except:?
A bare except: catches every possible exception, including ones you did not anticipate. This can silently swallow serious bugs like KeyboardInterrupt, making programs very difficult to debug.

Beyond the basics

A banking application uses exception handling so that if an error occurs during a transfer, the program catches it and prints "Transfer complete." to the user. Why is this a serious problem, and what should the developer do instead?
The problem: Telling the user "Transfer complete" when an exception was raised is misleading and potentially dangerous - money may not have moved but the user believes it has.

What to do instead:
1. Catch the specific exception and show an honest error message to the user.
2. Log the full exception details (type, message, stack trace) to a secure server log for developers to investigate.
3. Roll back any partial database changes so the account balances stay consistent.
4. Never hide errors behind misleading success messages - exception handling should recover gracefully, not lie.

Practice and Consolidation

Worksheet 1
Error Type Sorting
Classify each code snippet as a syntax, runtime or logic error and explain your reasoning.
Download PDF
Worksheet 2
Add Exception Handling
Rewrite programs that crash by adding appropriate try/except blocks and friendly error messages.
Download PDF
Worksheet 3
Exam-Style Questions
Trace exception flow, name specific exception types and answer extended response questions on robustness.
Download PDF

Programming Fundamentals Complete!

You have covered all 10 lessons - from variables and operators through to file handling and exception handling. Ready to test your full unit knowledge?

Back to series overview
Lesson 10 - Programming
Exception Handling
Starter activity
Show two versions of a division calculator: Version A crashes with a traceback when the user enters 0 or a letter; Version B handles both gracefully and asks the user to try again. Ask: "Which program would you rather use? Which would you rather release?" Use this to motivate why exception handling exists before any syntax is introduced.
Lesson objectives
1
Identify and distinguish syntax errors, runtime exceptions and logic errors with examples of each.
2
Use try, except and finally to handle runtime exceptions gracefully.
3
Name and recognise common exception types: ValueError, ZeroDivisionError, IndexError, FileNotFoundError, TypeError.
4
Raise exceptions deliberately using raise in Python and throw in C#.
5
Explain what is meant by "robust" code and why defensive programming matters in real systems.
Key vocabulary
exceptiontracebacktryexceptfinallyraise / throwValueErrorZeroDivisionErrorIndexErrorbare exceptsyntax errorruntime errorlogic errorrobustdefensive programming
Discussion questions
Why is a logic error harder to find than a syntax error? Can you think of a real example where a logic error could have serious consequences?
Why is it bad practice to use a bare except: that catches all errors? What problems can this hide?
When would it make sense to raise an exception yourself rather than just printing an error message?
Exit tickets
Write a try/except block that converts user input to an integer and catches a ValueError if the input is not a valid number. [3 marks]
Explain what the finally block does and give one situation where it is useful. [2 marks]
Give one example each of a syntax error, a runtime error and a logic error in Python. [3 marks]
Homework suggestion
Build a fully robust input validator: a program that (1) asks the user for two numbers, (2) divides the first by the second, (3) handles ValueError (non-numeric input), ZeroDivisionError (division by zero), and any other unexpected error with a generic catch-all. Display a clear, user-friendly message for each case. Extension: wrap the whole thing in a while loop so the user can keep trying until they get a valid result.