Handling Errors in Python: try, except, finally and raise

Python for Beginners: Learn to Code with Real Projects · Functions, Text and Files · 6 min read · Updated

try and except

  1. You put code that might fail inside a try block.
  2. If nothing goes wrong, the program continues normally.
  3. If something goes wrong, Python creates an exception and jumps to the matching except block.
  4. The except block handles the problem, for example with a clear message, and the program carries on. A finally block runs in both cases.

Things go wrong in real programs. A user types abc where you wanted a number. A file has been moved. A network is down. If you do nothing, Python stops with an error message called a traceback. A good program expects some of these problems and deals with them politely. Python uses exceptions for this, and the tools are try, except, finally and raise.

What is an exception?

When something goes wrong, Python creates an exception: an object that describes the problem, such as ValueError, ZeroDivisionError, FileNotFoundError, KeyError or IndexError. If nothing handles it, the program stops. With try and except, you say: try this, and if this kind of problem happens, do that instead.

def parse_amount(text):
    """Turn text like '1,250.50' into a number, or raise a clear error."""
    cleaned = text.strip().replace(",", "")
    try:
        value = float(cleaned)
    except ValueError:
        raise ValueError(f"'{text}' is not a number") from None
    if value < 0:
        raise ValueError("Amount cannot be negative")
    return value

for sample in ["1,250.50", "abc", "-5", " 40 "]:
    try:
        print(parse_amount(sample))
    except ValueError as error:
        print("Error:", error)

try:
    print(10 / 0)
except ZeroDivisionError:
    print("Cannot divide by zero")

try:
    with open("no_such_file.txt", encoding="utf-8") as f:
        f.read()
except FileNotFoundError as error:
    print("File problem:", error.filename)
finally:
    print("This always runs")

numbers = [1, 2]
try:
    print(numbers[5])
except IndexError as error:
    print(type(error).__name__, "-", error)

class BudgetError(Exception):
    pass

try:
    raise BudgetError("Over budget by 300")
except BudgetError as error:
    print("Caught:", error)
1250.5
Error: 'abc' is not a number
Error: Amount cannot be negative
40.0
Cannot divide by zero
File problem: no_such_file.txt
This always runs
IndexError - list index out of range
Caught: Over budget by 300

Reading the example

  • parse_amount cleans the text, tries float(...) and raises its own ValueError with a message that a user can understand. from None hides the confusing first error. It also rejects negative numbers by raising an error. The program prints the error with print("Error:", error).
  • In try, put only the line or lines that might fail. In except ValueError as error:, name the specific error you expect, and use as error to get the details.
  • ZeroDivisionError is caught so the program goes on.
  • For the missing file, error.filename tells us which file. The finally block always runs, whether there was an error or not. Use it for clean-up, such as closing a connection.
  • The IndexError handler prints the name of the error type with type(error).__name__.
  • BudgetError(Exception) is a custom exception: a class you make for a problem in your own program. It gives a clear name to your own errors, and lets other code catch exactly that error.

Rules for good error handling

  • Catch specific errors. Avoid a bare except: or except Exception: that hides every problem, including your own typing mistakes. A program that silently ignores errors is very hard to fix.
  • Keep `try` blocks small. If the block is long, you cannot tell which line failed.
  • Tell the user something useful: what went wrong and what to do.
  • Do not use exceptions for normal decisions when a simple if is clearer.
  • Let unexpected errors stop the program, or log them with details. Do not hide them.

else and finally

A try can also have an else block, which runs only if no error happened, and a finally block, which always runs. A common pattern is try to read, except for the expected problem, else to continue with the data. You will not need else often in your first programs, but it is good to recognise it.

Reading a traceback

When an error is not caught, Python prints a traceback: the chain of calls that led to the error, with file names and line numbers, from the oldest to the newest. Start with the last line, which names the error and gives the message. Then look at the line just above it, which is the place where it happened in your code. The lines above that show how the program got there. Copy the last line into a search engine when you are stuck, because many people have met the same error.

Validating input in a loop

A common beginner pattern is asking again until the input is good. Combine a while True loop, try and except, and break when the value is good.

while True:
    text = input("Enter your age: ")
    try:
        age = int(text)
    except ValueError:
        print("Please type a whole number, such as 25.")
        continue
    if 0 <= age <= 120:
        break
    print("Please enter an age from 0 to 120.")
print("Thanks, age saved:", age)

Your task

  1. Write safe_divide(a, b) that returns the result, or prints Cannot divide by zero and returns None when b is zero.
  2. Write the age loop above, and test it with abc, -5, 200 and 30.
  3. Write read_first_line(path) that returns the first line of a file, or the text File not found when it does not exist.
  4. Make a custom exception InvalidMarks and raise it when marks are below 0 or above 100.

How to check it worked

  • safe_divide(10, 0) prints the message and gives None.
  • The age loop asks again for the three wrong inputs and accepts 30.
  • You can explain why a bare except: is a bad idea.

Frequently asked questions

What is the difference between an error and an exception?

In everyday Python they mean almost the same. An exception is the object Python raises when something goes wrong, and you can catch it with try and except.

Should I catch every exception?

No. Catch the specific errors you expect and can handle. Hiding all errors makes bugs invisible and very hard to find.