Intermediate⏱ 20 min

Error Handling

Real programs fail — bad input, missing files, network timeouts. Learn to anticipate failures and handle them gracefully so your code never just crashes on the user.

Try / Except

Wrap risky code in a try block. If an exception is raised, Python jumps to the matching except block instead of crashing.

python
# Without error handling — crashes if input is "hello"
# number = int(input("Enter a number: "))  # ValueError!

# With error handling
def safe_divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("Error: cannot divide by zero")
        return None
    except TypeError as e:
        print(f"Error: wrong type — {e}")
        return None

print(safe_divide(10, 2))    # 5.0
print(safe_divide(10, 0))    # Error: cannot divide by zero → None
print(safe_divide(10, "x"))  # Error: wrong type → None

# Catch multiple types in one line
try:
    x = int("not a number")
except (ValueError, TypeError) as err:
    print(f"Conversion failed: {err}")
Output
⚠️
Never use bare except:Always catch specific exceptions. A bare except: silently catches everything — including keyboard interrupts and system exit — which makes debugging a nightmare.

Else and Finally

python
def read_number(text: str):
    try:
        result = int(text)
    except ValueError:
        print(f"  '{text}' is not a valid integer")
        return None
    else:
        # Runs ONLY if no exception occurred
        print(f"  Successfully parsed: {result}")
        return result
    finally:
        # ALWAYS runs — exception or not
        # Great for cleanup: close files, release locks, etc.
        print(f"  (attempted to parse '{text}')")

read_number("42")
print("---")
read_number("hello")
print("---")

# Real-world pattern: file handling
def read_file_safe(path: str):
    try:
        with open(path, "r") as f:
            return f.read()
    except FileNotFoundError:
        print(f"File not found: {path}")
        return ""
    except PermissionError:
        print(f"Permission denied: {path}")
        return ""
Output

Raising Exceptions

You can raise exceptions yourself with raise. This signals to calling code that something went wrong.

python
# Custom exception classes
class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        super().__init__(f"Need ${amount:.2f} but only have ${balance:.2f}")
        self.balance = balance
        self.amount  = amount

class NegativeAmountError(ValueError):
    pass

class BankAccount:
    def __init__(self, balance=0.0):
        self.balance = float(balance)

    def withdraw(self, amount: float):
        if amount <= 0:
            raise NegativeAmountError(f"Amount must be positive, got {amount}")
        if amount > self.balance:
            raise InsufficientFundsError(self.balance, amount)
        self.balance -= amount
        return self.balance

acc = BankAccount(100)

try:
    acc.withdraw(150)
except InsufficientFundsError as e:
    print(f"Failed: {e}")
except NegativeAmountError as e:
    print(f"Bad input: {e}")

print(f"Balance unchanged: ${acc.balance}")
Output

Common Built-in Exceptions

ExceptionWhen it occurs
ValueErrorRight type, wrong value — int("hello")
TypeErrorWrong type — "hi" + 5
KeyErrorKey not in dict — d["missing"]
IndexErrorIndex out of range — lst[99]
AttributeErrorObject doesn't have that attribute/method
FileNotFoundErrorFile or directory not found
ZeroDivisionErrorDividing by zero
ImportErrorModule not found or can't be imported
🎉

Intermediate complete!

Next: Decorators — your first step into advanced Python.

🏆

Certificate Unlocked!

You completed all lessons with 70%+. View your certificate →