PYTHON FOR AI • LESSON 5

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.

CORE IDEA

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.

01

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.

02

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.

03

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.

04

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.

05

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.

06

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."
07

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.

08

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.

09

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
10

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())
11

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.

12

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.

13

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.

14

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.

15

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.

16

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.

17

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.

18

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
19

What You Learned

  • What errors and exceptions are
  • try blocks
  • except blocks
  • Handling specific exceptions
  • Using as to inspect errors
  • Multiple exception handlers
  • else
  • finally
  • HTTP API errors
  • Network errors
  • Timeout errors
  • JSON parsing errors
  • raise_for_status()
  • Error logging
  • Error handling in AI applications
KEY TAKEAWAY

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.