Python Try...Except: Handle Errors and Exceptions - Python with AI tutorial chapter 23
Python

Python Try…Except: Handle Errors and Exceptions

Python with AI Tutorial · Chapter 23 of 48

Python try except is the mechanism for handling errors without crashing your program. Code that might fail goes in a try block; if an exception occurs, Python jumps to the matching except block instead of stopping. Optional else and finally clauses run when no error occurred and when the block finishes, respectively.

Why exceptions matter

When something goes wrong, such as dividing by zero or opening a missing file, Python raises an exception. If nothing catches it, the program prints a traceback and exits. In a report that processes hundreds of records, one bad row should not throw away the other 499.

revenue = 12000
units_sold = 0
print(revenue / units_sold)
Output:
Traceback (most recent call last):
  File "report.py", line 3, in <module>
    print(revenue / units_sold)
ZeroDivisionError: division by zero

The last line of a traceback names the exception type and gives a message. Read tracebacks from the bottom up: the type tells you what category of problem occurred, and the lines above show where.

Basic try…except

Wrap the risky code in try and name the exception you expect in except. If that exception is raised, the handler runs and the program continues.

revenue = 12000
units_sold = 0
try:
    price = revenue / units_sold
    print(f"Average price: {price:.2f}")
except ZeroDivisionError:
    print("No units sold this period, average price not available")
print("Report continues")
Output:
No units sold this period, average price not available
Report continues

Notice that the print() inside the try block did not run either. As soon as the division fails, Python abandons the rest of the try block and looks for a handler. Anything you want to run only on success belongs in the else clause, covered below.

Common built-in exceptions

Python has dozens of exception types. These are the ones you will meet most often in data and business scripts.

Exception Raised when Typical example
ValueError Right type, wrong value int("12.5kg")
TypeError Operation on an incompatible type "5" + 3
KeyError Dictionary key missing prices["Desk"]
IndexError List index out of range items[10] on a 3-item list
ZeroDivisionError Dividing by zero total / 0
FileNotFoundError File path does not exist open("missing.csv")
AttributeError Object lacks that attribute or method None.upper()

Handling several exception types

You can stack multiple except blocks, or catch several types in one block with a tuple. Python uses the first handler that matches, so put the most specific ones first.

raw_values = ["1200", "abc", "850", None, "430"]
total = 0
for raw in raw_values:
    try:
        total += int(raw)
    except ValueError:
        print(f"Skipping non-numeric value: {raw!r}")
    except TypeError:
        print("Skipping missing value")
print("Total:", total)
Output:
Skipping non-numeric value: 'abc'
Skipping missing value
Total: 2480

Getting the error message with as

Add as e to keep the exception object. Printing it shows the message, which is often exactly what you want to log.

prices = {"Chair": 1450, "Desk": 3900}
for item in ["Chair", "Lamp"]:
    try:
        print(item, prices[item])
    except KeyError as e:
        print(f"Price list has no entry for {e}")
Output:
Chair 1450
Price list has no entry for 'Lamp'
Common mistake: a bare except: with no type catches everything, including KeyboardInterrupt and typos in your own variable names. It hides real bugs. Always name the exception, or at minimum use except Exception as e: and log e.

else and finally

The else block runs only when the try block raised nothing. The finally block runs no matter what, which makes it the right place for clean-up such as closing files or connections.

def load_rate(rates, currency):
    try:
        rate = rates[currency]
    except KeyError:
        print(f"{currency}: no rate, using 1.0")
        rate = 1.0
    else:
        print(f"{currency}: rate found")
    finally:
        print("Lookup finished")
    return rate

rates = {"USD": 83.5, "EUR": 90.2}
print(load_rate(rates, "EUR"))
print(load_rate(rates, "GBP"))
Output:
EUR: rate found
Lookup finished
90.2
GBP: no rate, using 1.0
Lookup finished
1.0
Try it with AI

Paste a traceback you do not understand and ask the assistant to explain it line by line and to suggest the narrowest except clause that would handle it.

Here is a Python traceback from my invoice script: [paste traceback]. Explain what each line means, reading from the bottom up, tell me which exception type I should catch and why catching Exception or a bare except would be a bad idea here, and show the corrected try/except block.

In practice, finally is how you guarantee resources are released. If a function opens a database connection or a file and then raises halfway through, the finally block still closes it. The with statement, introduced in the file-handling chapter, is built on the same idea.

Raising your own exceptions

Use raise to signal a problem yourself. Choose the built-in type that best describes the issue, and give a clear message. This turns silent bad data into an error the caller can handle.

def set_discount(percent):
    if not 0 <= percent <= 100:
        raise ValueError(f"Discount must be 0-100, got {percent}")
    return percent / 100

for p in (15, 120):
    try:
        print(set_discount(p))
    except ValueError as e:
        print("Rejected:", e)
Output:
0.15
Rejected: Discount must be 0-100, got 120

Custom exception classes

For errors specific to your business rules, define your own exception by subclassing Exception. Callers can then catch exactly that problem and let unrelated errors bubble up.

class InsufficientStockError(Exception):
    pass

def ship(item, qty, stock):
    if stock[item] < qty:
        raise InsufficientStockError(f"Only {stock[item]} {item} left, {qty} requested")
    stock[item] -= qty
    return stock[item]

stock = {"monitor": 5}
try:
    ship("monitor", 8, stock)
except InsufficientStockError as e:
    print("Order held:", e)
Output:
Order held: Only 5 monitor left, 8 requested

Because InsufficientStockError inherits from Exception, a generic except Exception higher up would still catch it. The custom class simply gives you a more precise option.

Try it with AI

Ask the assistant to add robust error handling to a loop that reads mixed-quality data, and to explain why each exception is handled where it is.

Write a Python function parse_orders(rows) that takes a list of dictionaries with keys "id", "qty" and "price" as strings. Convert qty to int and price to float, skip rows with missing keys or bad numbers, collect the failures in a separate list with a reason, and return (good_rows, failures). Use specific except clauses for KeyError, ValueError and TypeError and explain why you did not use a bare except.

A good habit is to keep the try block as small as possible: only the one or two lines that can actually fail. This makes it obvious which operation the handler is protecting and stops unrelated bugs from being caught by accident.

Common mistakes

  • Using a bare except: that swallows every error, including programming mistakes.
  • Wrapping far too much code in one try, so you cannot tell which line failed.
  • Catching an exception and doing nothing (pass) so the failure disappears silently.
  • Catching Exception before a more specific handler, which makes the specific handler unreachable.
  • Using exceptions for normal control flow when a simple if check would be clearer.

Exercise

Write a function safe_average(values) that returns the average of a list of numbers. It should return None and print a message if the list is empty (ZeroDivisionError) and skip any item that cannot be converted to float. Test it with ["10", "20", "x", "30"] and [].

Show answer
def safe_average(values):
    clean = []
    for v in values:
        try:
            clean.append(float(v))
        except (ValueError, TypeError):
            print(f"Skipping {v!r}")
    try:
        return sum(clean) / len(clean)
    except ZeroDivisionError:
        print("No valid numbers")
        return None

print(safe_average(["10", "20", "x", "30"]))
print(safe_average([]))
Output:
Skipping 'x'
20.0
No valid numbers
None

Related chapters

FAQ

What is the difference between except Exception and a bare except in Python?

except Exception catches ordinary runtime errors but lets system-level signals such as KeyboardInterrupt and SystemExit through. A bare except: catches those too, which can make a program impossible to stop cleanly.

When does the finally block run in Python?

Always: after the try block succeeds, after an except handler runs, and even if a return or an unhandled exception leaves the block. It is intended for clean-up work.

How do I raise an exception in Python?

Use raise followed by an exception instance, for example raise ValueError("Quantity must be positive"). You can also define your own class that inherits from Exception and raise that.

Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.

Chapter 23 of 48 · Python with AI: all 48 chapters

PK
Meet PK, the founder of NeotechNavigators.com! With over 15 years of experience in Data Visualization, Excel Automation, and dashboard creation. PK is a Microsoft Certified Professional who has a passion for all things in Excel. PK loves to explore new and innovative ways to use Excel and is always eager to share his knowledge with others. With an eye for detail and a commitment to excellence, PK has become a go-to expert in the world of Excel. Whether you're looking to create stunning visualizations or streamline your workflow with automation, PK has the skills and expertise to help you succeed. Join the many satisfied clients who have benefited from PK's services and see how he can take your data analysis skills to the next level!
https://neotechnavigators.com