Error Handling
Programs do not always run successfully. APIs can be unavailable, URLs can be wrong, authentication can fail, and servers can return errors. In this lesson, you will learn how to detect errors, handle exceptions, and build Python API code that fails safely instead of crashing unexpectedly.
Error handling lets your program respond to problems instead of crashing.
In Python, you commonly use try, except, else, and finally to control what happens when something goes wrong.
What Is an Error?
An error happens when Python cannot successfully perform an operation.
For example:
number = 10 result = number / 0 print(result)
Python cannot divide a number by zero, so it raises an exception.
ZeroDivisionError
Without error handling, the program stops at the error.
What Is an Exception?
In Python, many runtime problems are represented as exceptions.
Some common exceptions are:
- ValueError — invalid value
- TypeError — incorrect data type
- KeyError — dictionary key does not exist
- IndexError — list index does not exist
- FileNotFoundError — file does not exist
- ZeroDivisionError — division by zero
You can catch these exceptions and decide how your program should respond.
The try Block
Put code that might fail inside a
try block.
try:
number = 10 / 0
except:
print("Something went wrong.")
Something went wrong.
Instead of allowing the exception to stop the entire
program, Python moves to the except
block.
The except Block
The except block defines what your
program should do when an exception occurs.
try:
number = 10 / 0
except ZeroDivisionError:
print("You cannot divide by zero.")
You cannot divide by zero.
This is better than catching every possible exception because you are explicitly handling the problem you expect.
Reading the Exception
You can store the exception object using
as.
try:
number = 10 / 0
except ZeroDivisionError as error:
print("Error:", error)
Error: division by zero
This is useful when you need more information about what went wrong.
Handling Different Exceptions
A single operation can potentially produce different types of exceptions.
try:
number = int(input("Enter a number: "))
result = 100 / number
print(result)
except ValueError:
print("Please enter a valid number.")
except ZeroDivisionError:
print("Number cannot be zero.")
Now the program handles two different problems separately.
Invalid text
↓
ValueError
↓
"Please enter a valid number."
Zero
↓
ZeroDivisionError
↓
"Number cannot be zero."
The else Block
The else block runs only when no exception
occurs.
try:
number = int("10")
except ValueError:
print("Invalid number.")
else:
print("Conversion successful.")
print(number)
Conversion successful. 10
This can make your code easier to understand because successful logic is separated from error handling.
The finally Block
The finally block runs whether an
exception occurs or not.
try:
number = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero.")
finally:
print("Operation finished.")
Operation finished.
It is commonly useful for cleanup operations.
Complete Error Handling Structure
try:
# Code that might fail
except SomeError:
# Handle the error
else:
# Runs when no error occurs
finally:
# Always runs
try
↓
Did an error happen?
↓
YES ─────→ except
↓
Handle error
NO
↓
else
↓
Continue successfully
finally
↓
Cleanup
API Errors
API applications have another important category of errors: HTTP errors.
For example:
200 ↓ Success 400 ↓ Bad Request 401 ↓ Authentication Problem 403 ↓ Forbidden 404 ↓ Not Found 500 ↓ Server Error
The Python Requests library provides
raise_for_status() to turn unsuccessful
HTTP responses into exceptions.
import requests
response = requests.get(
"https://api.example.com/products",
timeout=10
)
response.raise_for_status()
print(response.json())
Handling HTTP Errors
You can catch Requests-specific exceptions.
import requests
try:
response = requests.get(
"https://api.example.com/products",
timeout=10
)
response.raise_for_status()
data = response.json()
print(data)
except requests.exceptions.HTTPError as error:
print("HTTP error:", error)
Now an HTTP error such as a 404 or 500 can be handled instead of unexpectedly stopping the application.
Network Errors
The server might not be reachable at all.
For example, the user's internet connection could fail or the server could be unavailable.
import requests
try:
response = requests.get(
"https://api.example.com/products",
timeout=10
)
response.raise_for_status()
print(response.json())
except requests.exceptions.ConnectionError:
print("Could not connect to the server.")
except requests.exceptions.Timeout:
print("The request timed out.")
This distinction matters because a network failure is different from an API returning a 404 or 500.
Handling JSON Errors
You should not blindly assume every API response is valid JSON.
import requests
try:
response = requests.get(
"https://api.example.com/products",
timeout=10
)
response.raise_for_status()
data = response.json()
print(data)
except requests.exceptions.JSONDecodeError:
print("The server returned invalid JSON.")
This becomes important when working with real-world APIs because external systems do not always behave exactly as expected.
Practical API Error Handling
Now combine the important error types into one example.
import requests
url = "https://api.example.com/products"
try:
response = requests.get(
url,
timeout=10
)
response.raise_for_status()
data = response.json()
print("API request successful.")
print(data)
except requests.exceptions.Timeout:
print("The request took too long.")
except requests.exceptions.ConnectionError:
print("Could not connect to the API.")
except requests.exceptions.HTTPError as error:
print("HTTP error:", error)
except requests.exceptions.JSONDecodeError:
print("Invalid JSON response.")
This gives your application different responses for different types of failures.
Should You Use a Generic except?
You can write:
try:
do_something()
except:
print("Something went wrong.")
But this is usually a bad habit in production code. It catches almost everything and can hide programming mistakes.
Prefer specific exceptions when you know what can go wrong.
try:
number = int("hello")
except ValueError:
print("Invalid number.")
Specific error handling makes debugging much easier.
Error Handling and Logging
Printing an error is useful while learning, but real applications often use logging.
import logging
logging.basicConfig(
level=logging.ERROR
)
try:
number = 10 / 0
except ZeroDivisionError as error:
logging.error(
"Calculation failed: %s",
error
)
Logging becomes especially useful for AI applications and API clients because failures can happen outside your own code.
Error Handling in AI Applications
AI applications depend heavily on external services. That means error handling is not optional.
AI Application
↓
Send API Request
↓
┌───────────────┐
│ │
Success Error
│ │
↓ ↓
JSON Response Handle Problem
│ │
↓ ↓
Continue Retry / Message /
Log / Stop
For example, an AI API might temporarily be unavailable. Your application should not simply crash and show a Python traceback to the user.
Mini Project — Safe API Client
Build a small API client that handles the most common problems.
import requests
def get_products():
url = "https://api.example.com/products"
try:
response = requests.get(
url,
timeout=10
)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print("Request timed out.")
except requests.exceptions.ConnectionError:
print("Could not connect to the API.")
except requests.exceptions.HTTPError as error:
print("HTTP error:", error)
except requests.exceptions.JSONDecodeError:
print("Invalid JSON response.")
return None
data = get_products()
if data is not None:
print("Products:")
print(data)
else:
print("Could not retrieve products.")
Call API
↓
Request successful?
│
├── YES → Parse JSON → Return data
│
└── NO
↓
Identify error
↓
Handle error
↓
Return safely
What You Learned
- What errors and exceptions are
tryblocksexceptblocks- Handling specific exceptions
- Using
asto inspect errors - Multiple exception handlers
elsefinally- HTTP API errors
- Network errors
- Timeout errors
- JSON parsing errors
raise_for_status()- Error logging
- Error handling in AI applications
Good error handling makes your application predictable when things go wrong.
Remember the basic pattern: try → perform risky operation → except → handle the problem → else → continue successful work → finally → cleanup. When working with APIs, also handle HTTP errors, connection failures, timeouts, and invalid responses.