7. Error Handling#

7.1. What are Errors?#

In programming, errors (also called exceptions) are problems that occur during the execution of a programme.

If errors are not handled, they can cause the programme to crash or to not carry out the code as intended.

There are different types of errors which can be sorted into the following categories:

  • Syntax errors: Mistakes in the code that prevent Python from understanding it since it violates its rules (e.g., missing a colon).

  • Logical errors: Mistakes in using the correct logic leading to wrong outputs (e.g. using > instead of <)

  • Runtime errors: Problems that happen while the programme is running preventing it from compiling (e.g. dividing by zero).

  • Time Limit Exceeded error: Error when the code take to long to execute (e.g stuck in a while loop)

# Syntax error example (missing colon):
if 5 > 2
    print("Five is greater than two")
  Cell In[1], line 2
    if 5 > 2
            ^
SyntaxError: expected ':'
# Runtime error example (dividing by zero):
a = 5
b = 0
print(a / b)  # This will cause a ZeroDivisionError
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[5], line 8
      6 a = 5
      7 b = 0
----> 8 print(a / b)  # This will cause a ZeroDivisionError

ZeroDivisionError: division by zero

7.2. Using try-except Blocks#

You can catch and handle errors using a try-except block. This allows the programme to continue running even if something goes wrong.

In the previous chapter, we saw how conditional blocks (if-else) allow a programme to choose between different actions. Similarly, try-except blocks allow a programme to react differently depending on whether an error occurs during execution. Python tries to execute the code inside the try: block. If an error occurs while executing the code in the block, it jumps to the except: block.

a = 5
b = 0

try:
    result = a / b
    print(result)
except:
    print("Cannot divide by zero!")
Cannot divide by zero!

Tip About Specific Exceptions

It is good practice to catch specific exceptions, like ZeroDivisionError, instead of using a general except: whenever possible. This avoids hiding unexpected errors that you did not intend to catch.

7.3. Catching Multiple Exception Types#

You can handle multiple different errors using multiple except blocks with each stating the ErrorName (e.g. ValueError) in the condition statement. Python checks each except block in order and runs the first one that matches the error.

The exact error message can also be saved in a variable error_variable by adding an as error_variable at the end of the except statement, e.g. except ValueError as value_error:. You can also just write except: or except Exception as error_variable: to handle any error (and capture it).

try:
    x = int(input("Enter a number: "))
    result = 10 / x
except ValueError as value_error:
    # This will catch the ValueError if the input is not a valid integer
    print("x is not a valid number. Error message:", value_error)
except ZeroDivisionError as zero_division_error:
    # This will catch the ZeroDivisionError if x is zero
    print("You cannot divide by zero. Error message:", zero_division_error)
except Exception as expected_error:
    print(f"An unexpected error occurred: {expected_error}")

7.4. Other try-except associated blocks#

The finally block always runs — whether an error occurred or not. It can be added as a block after try-except. You can use it to clean up actions, such as closing files or displaying a final message.

In the following example, "Execution complete." is printed no matter what happens inside the try.

try:
    x = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")
finally:
    print("Execution completed.")
Cannot divide by zero!
Execution completed.

You can also add an else block after a try-except. The else block runs only if no exception was raised.

Tip

The else block is useful when you want to separate the code that runs when everything goes smoothly from the code that handles errors.

try:
    number = int(input("Enter a number: "))
except ValueError:
    print("Invalid input.")
else:
    print(f"You entered: {number}")
You entered: 10

7.5. Logic errors#

try-except statements are very useful but they can only capture errors that interrupt the code / hinder it from being carried out. Often the code syntax is completely correct but its output is still wrong. These are due the code having a logic error somewhere. These errors are often a lot harder to capture or one might not even be aware of them. Make sure that you output your intermediate results, double-check all your code (for example by testing a sub-part of it whether it does what it should do - these are called Unit tests which are out of the scope of this tutorial but there exist various sources online), and add useful comments and variable names to reduced the chance of getting a logic error and/ or not being able to identify where it comes from.

7.6. Quick Practice#

Try this challenge:

  • Create two variables, a and b, where b is initially set to 0.

  • Write a try-except block that tries to divide a by b.

  • Catch and handle the ZeroDivisionError by printing a friendly error message and the actual message returned.

  • Add a finally block that prints "Finished division attempt.".

# Put your code here
💡 Solution
a = 10
b = 0

try:
    result = a / b
except ZeroDivisionError as error_message:
    print("You tried to divide by zero.")
    print("The error message was:", error_message)
finally:
    print("Finished division attempt.")