Ever had your Python script crash because of a simple error? Frustrating, isn’t it? Especially when all you want is for your code to just work.
Now, imagine your program catching those errors, staying calm, and handling them like a pro. That’s exactly what Python’s try-except
does for you. It keeps your scripts running smoothly—even when something goes wrong.
Whether you’re automating tasks, working with files, or managing user input, try-except
is your go-to tool for handling errors gracefully. No more debugging chaos or broken workflows. Just clean, reliable code that saves you time and makes your work more efficient.
In this guide, I’ll show you how to use try-except
step by step, with examples you can try out right away. Ready to make your code error-proof?
Let’s get started!
Sidenote: If you find any of this confusing, or simply want a deep dive into Python, check out Andrei's Python Coding course taken by 200,000+ people:
It’ll take you from an absolute beginner and teach you everything you need to be hired ASAP.
Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:
It'll show you how to automate all of the boring or repetitive tasks in you life - and makes for some pretty stand out portfolio projects!
With that out of the way, let's get into this 5-minute tutorial!
Let’s say you’re running a Python script to process a bunch of files. What happens if one of those files is missing or in the wrong format? Without error handling, your program crashes—leaving you with zero results and a lot of frustration.
That’s where Python’s try-except
saves the day.
With try-except
, you don’t have to worry about your script breaking when things go wrong. Instead of testing everything in advance, you let the program handle issues as they happen.
It’s like saying, “Let’s give this code a shot, and if it fails, I’ll handle it.” Your program stays in control and keeps running smoothly.
Here’s an example:
Imagine dividing numbers, but by mistake, you divide by zero. Normally, Python would throw a ZeroDivisionError
and stop everything. Using try-except
, you can catch that error and respond to it.
Check this out:
try:
result = 10 / 0
except ZeroDivisionError:
print("You can’t divide by zero!")
See how the program doesn’t crash? Instead, it prints a friendly message and moves on.
This approach is a lifesaver when you’re automating tasks. Files can go missing, users might enter invalid data, or a server could fail to respond. But with try-except
, your code handles these surprises gracefully and keeps working - saving you time and headaches.
When it comes to catching and handling errors in your code, try-except
is your best friend. Let’s go through it step by step, with examples you can follow along with.
The try
block is where you put the code you want Python to run. If everything works, great! But if there’s an error, Python will skip to the except
block.
Here’s a quick example:
try:
number = int(input("Enter a number: "))
print(f"The number you entered is {number}.")
This code tries to convert your input into an integer, so if you type something like 42
, it works perfectly.
But what if you type hello
? That’s where the next step comes in.
The except
block is your safety net. If something goes wrong in the try
block, this is where you handle it.
For example
Let’s handle invalid input that results in a ValueError
:
try:
number = int(input("Enter a number: "))
print(f"The number you entered is {number}.")
except ValueError:
print("That’s not a valid number. Please try again!")
Now, instead of your program crashing when someone types hello
, it prints a helpful message and keeps going. Handy, right?
Sometimes, you’ll need to handle multiple types of errors. You can stack except
blocks to catch each one separately:
try:
result = 10 / int(input("Enter a number: "))
print(f"The result is {result}.")
except ZeroDivisionError:
print("You can’t divide by zero!")
except ValueError:
print("That’s not a valid number.")
This way, whether the user divides by zero or types gibberish, your program has a response for both.
The else
block is like a little celebration. It only runs if the try
block succeeds—no errors at all. This keeps your error handling separate from your success logic:
try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input!")
else:
print(f"Success! You entered {number}.")
The finally
block runs no matter what happens, making it perfect for cleanup tasks like closing files or freeing up resources:
try:
file = open("data.txt", "r")
content = file.read()
print(content)
except FileNotFoundError:
print("File not found.")
finally:
if 'file' in locals() and not file.closed:
file.close()
print("Closing the file.")
Even if the file doesn’t exist, Python still ensures the file gets closed. This is a must-have for automation scripts that work with files, databases, or other resources.
Simple right?
OK, let's go into a more complex project and show how it works.
When you’re working on automating tasks—like calling an API—things don’t always go as planned. Maybe the server times out, the data isn’t in the format you expect, or the connection drops entirely.
Frustrating, right?
But with a bit of planning and Python’s try-except
, you can handle these hiccups gracefully.
Let’s break it down step by step.
Note: To run these examples, you’ll need the requests library. Install it using pip install requests.
The first step is to go through and try to figure out what could go wrong, so that we can then deal with this.
So let's take a look at a few potential issues...
Maybe the server is down or the connection fails.
try:
response = requests.get("https://api.example.com/users", timeout=5)
except requests.exceptions.Timeout:
print("The request timed out.")
except requests.exceptions.ConnectionError:
print("Failed to connect to the API.")
HTTP codes like 404 (Not Found) or 500 (Internal Server Error) mean something went wrong on the server.
# Check for HTTP errors (e.g., 404 or 500)
response.raise_for_status()
# Parse the API response as JSON
data = response.json()
# Ensure the response contains the expected "users" key
if "users" not in data:
raise KeyError("Key 'users' not found in the response.")
The API might return data that’s missing fields or isn’t formatted properly.
# Check for HTTP errors (e.g., 404 or 500)
response.raise_for_status()
# Parse the API response as JSON
data = response.json()
# Ensure the response contains the expected "users" key
if "users" not in data:
raise KeyError("Key 'users' not found in the response.")
Spotting these issues upfront helps you write a script that can adapt instead of crashing.
Once you know what could fail, now it's time to decide what to do when it happens. Let's look at some solutions for the potential issues we just mapped out.
If a timeout occurs, try the request again.
for attempt in range(3): # Retry up to 3 times
try:
response = requests.get("https://api.example.com/users", timeout=5)
break # Exit the loop if successful
except requests.exceptions.Timeout:
print(f"Attempt {attempt + 1} timed out. Retrying...")
Keep track of what went wrong so you can fix it later.
import logging
logging.basicConfig(filename="errors.log", level=logging.ERROR)
try:
response = requests.get("https://api.example.com/users")
response.raise_for_status()
except requests.exceptions.HTTPError as e:
logging.error(f"HTTP error: {e}")
Check that the response contains everything you need before using it.
try:
data = response.json()
if "users" not in data:
raise KeyError("Missing 'users' key in response data.")
except KeyError as e:
logging.error(f"Data validation error: {e}")
Here’s how you can handle all these issues in one script:
import requests
import logging
import time
# Configure logging
logging.basicConfig(filename="automation.log", level=logging.ERROR)
def fetch_user_data():
url = "https://api.example.com/users"
for attempt in range(3):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
data = response.json()
if "users" not in data:
raise KeyError("Key 'users' not found in the response.")
return data["users"]
except requests.exceptions.Timeout:
logging.error(f"Timeout on attempt {attempt + 1}")
time.sleep(2)
except requests.exceptions.RequestException as e:
logging.error(f"Request error: {e}")
return None
except KeyError as e:
logging.error(f"Data validation error: {e}")
return None
return None
# Call the function and check the results
users = fetch_user_data()
if users:
print(f"Fetched {len(users)} users.")
else:
print("Failed to fetch user data after 3 attempts.")
Simple!
So as you can see, even if you're writing advanced code - by planning for potential issues, you create scripts that don’t just survive errors - they thrive in real-world conditions.
Writing clean, reliable code is about more than just making it work—it’s about making it easy to maintain and debug. Here are six tips to help you get the most out of Python’s try-except
.
You’ve probably seen a generic except
block before, but here’s the thing: it catches everything—including errors you weren’t expecting. Instead, focus on catching the specific errors you want to handle.
Why does this matter? If an unexpected bug sneaks in, it’s better to see it crash and fix it rather than silently masking it.
For example:
try:
number = int(input("Enter a number: "))
except ValueError:
print("That’s not a valid number.")
This way, only invalid inputs trigger the exception. Errors like KeyboardInterrupt
are left alone, so your program behaves as expected.
When something goes wrong, don’t just show an error message—log it. Logging creates a record of what happened, making it easier to debug issues later.
Why is this helpful? You can check the logs to understand what went wrong, even if you weren’t watching the program when it failed.
For example:
import logging
logging.basicConfig(filename="errors.log", level=logging.ERROR)
try:
result = 10 / 0
except ZeroDivisionError as e:
logging.error(f"Error occurred: {e}")
Now you’ve got a detailed error report saved to a file—perfect for debugging.
If your code uses resources like files or database connections, the finally
block ensures they’re released no matter what.
Why is this important? Without proper cleanup, you risk memory leaks, locked files, or worse.
For example:
try:
number = int(input("Enter a number: "))
except ValueError:
print("That’s not a valid number.")
else:
print(f"You entered {number}, which is valid!")
Even if there’s an error, the file is safely closed.
It might be tempting to wrap everything in a try-except
, but that can make your code messy and harder to understand. Focus on using it only for risky parts like file handling, user input, or API calls.
Here’s the difference:
This example unnecessarily wraps the entire operation, including file reading and cleanup, in a try-except
.
While it handles the FileNotFoundError
, it also complicates the code with a finally block
for resource cleanup. This approach might lead to confusion, especially when there are more efficient alternatives.
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found.")
finally:
file.close()
print("File closed.")
Why it’s messy:
Compare that to this cleaner and better example...
This example focuses the try-except
on risky operations—user input and division—while separating success and error handling logic. It’s clear and easy to read.
try:
number = int(input("Enter a number: "))
result = 10 / number
except ValueError:
print("That’s not a valid number.")
except ZeroDivisionError:
print("You can’t divide by zero.")
else:
print(f"The result is {result}.")
Why it’s better:
except
block addresses specific risks (invalid input and division by zero), keeping the code focusedelse
block, while errors are handled separately, making the intent clearExceptions like KeyboardInterrupt
or SystemExit
are special—they’re meant to stop your program. Catching them accidentally can cause trouble, so handle them thoughtfully.
For example
try:
while True:
print("Running...")
except KeyboardInterrupt:
print("Stopped by user.")
Errors don’t have to stop you in your tracks. With Python’s try-except
, you can handle mistakes gracefully, keep your programs running, and save yourself hours of debugging frustration.
Now it’s your turn. Take what you’ve learned here and try adding try-except
to one of your scripts. Start small—maybe handle invalid user input or catch a missing file. As you practice, you’ll see just how powerful and flexible this tool is for making your code more reliable.
Got a project in mind? Open up your editor and give it a go. The best way to learn is by doing, and try-except
is one skill you’ll use over and over again.
Remember - If you want to dive deep into Python then be sure to check out Andrei's Complete Python Developer course:
It’ll take you from an absolute beginner and teach you everything you need to get hired ASAP and ace the tech interview.
This is the only Python course you need if you want to go from complete Python beginner to getting hired as a Python Developer this year!
Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:
It'll show you how to automate all of the boring or repetitive tasks in you life - and makes for some pretty stand out portfolio projects!
Plus, as part of your membership, you'll get access to both of these courses and others, and be able to join me and 1,000s of other people (some who are alumni mentors and others who are taking the same courses that you will be) in the ZTM Discord.
Ask questions, help others, or just network with other Python Developers, students, and tech professionals.
If you enjoyed this post, check out my other Python tutorials: