Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 15

Handling Custom Exceptions in FastAPI

Recommended resources

Hello! Welcome back to our course on building APIs with FastAPI.

In the last lesson, we mastered how to create flexible endpoints that combine path parameters, query parameters, and request bodies. This is a crucial pattern for building complex and feature-rich APIs.

Today, we'll address a fundamental aspect of creating robust applications: handling errors. While FastAPI automatically handles data validation errors (like we saw with Pydantic), you'll often need to signal errors that arise from your application's own logic. This lesson focuses on the learning outcome: Raise HTTPException to return custom error responses with specific status codes.

By the end of this lesson, you will be able to gracefully handle situations like a requested item not being found, and communicate that failure clearly back to the API client.

1. The Problem: Why a Simple return Isn't Enough

Imagine you have an endpoint /items/{item_id}. What happens if a client requests an ID that doesn't exist in your database? A first instinct might be to return a simple dictionary like {"error": "Item not found"}. However, there's a critical flaw in this approach.

When you return a value from an endpoint, FastAPI, by default, sends it with a 200 OK HTTP status code. This code signals "success" to the client application, even though the operation actually failed. The client would then have to inspect the JSON body to figure out if it was a real success or a hidden error, which is inefficient and breaks standard API conventions.

Let's watch a video that illustrates this exact problem and explains why using the correct HTTP status code is so important for building predictable, reliable APIs.

Python FastAPI Tutorial (Part 3): Path Parameters - Validation and Error Handling

This video from Corey Schafer's Python FastAPI Tutorial clearly demonstrates why simply returning an error message with a 200 OK status is bad practice.

Watch from 01:22 to 06:52. The video starts by building an endpoint to get a post by its ID. Pay close attention to the section from 04:50 onwards, where he explains the problem with the 200 OK response for a non-existent post.


2. The Solution: Raising HTTPException

FastAPI provides an elegant and standard way to handle these business-logic errors: the HTTPException.

HTTPException is a special Python exception. Instead of returning it, you raise it. When FastAPI catches an HTTPException, it halts the processing of the request and sends a properly formatted HTTP error response to the client, including the status code and a JSON body with details you provide.

Let's look at the official documentation to see the fundamental pattern.

Handling Errors - FastAPI

This guide from the official FastAPI documentation is the canonical source for learning about HTTPException. It explains the concept and provides a clear, minimal code example.

Read the sections 'Use HTTPException', 'Import HTTPException', and 'Raise an HTTPException in your code'. Focus on the code example and the explanation of why you raise an exception instead of returning it.

The core of the implementation looks like this:

FastAPI HTTPException Example
This code shows the standard pattern: import `HTTPException`, check for an error condition (like a missing item), and `raise HTTPException` with a specific `status_code` and a `detail` message if the condition is met.

The two most important parameters for HTTPException are:

  • status_code: An integer representing the HTTP status code (e.g., 404 for "Not Found").
  • detail: A message for the client explaining the error. This can be a simple string or even a more complex JSON-serializable object like a dictionary.

Now, let's see how the incorrect implementation from the previous video is fixed using this exact technique.

Python FastAPI Tutorial (Part 3): Path Parameters - Validation and Error Handling

Continuing with Corey Schafer's tutorial, this segment shows how to correctly implement the error handling using HTTPException.

Watch from 06:52 to 09:53. You will see how to import HTTPException and status, and how to raise the exception with a 404 Not Found status. Notice how the response in the browser now correctly shows the 404 status code.


3. Common HTTP Error Codes

While 404 Not Found is very common, there are several other 4xx-range status codes you will use regularly to signal different types of client-side errors. Using the correct code is key to building a RESTful API.

Here are a few essential ones:

Status Code Name When to Use
400 Bad Request For general client-side errors where the request payload is syntactically correct but semantically invalid. For example, trying to create a resource with a name that is already taken.
401 Unauthorized The request requires user authentication, but the client has not provided valid credentials. We'll use this when we build authentication.
403 Forbidden The client is authenticated, but they do not have the necessary permissions to access the requested resource or perform the action.
404 Not Found The server cannot find the requested resource (e.g., /users/999 where user 999 doesn't exist). This is what we've focused on today.

For a quick look at HTTPException being used with a 400 status code, you can watch this short clip.

How to Use FastAPI: A Detailed Python Tutorial

This clip from ArjanCodes shows another practical example of HTTPException, this time for a 400 Bad Request when trying to create a resource that already exists.

Watch from 10:34 to 11:05. This demonstrates how to prevent the creation of a duplicate item by raising a 400 error.


4. Hands-On Practice

Let's apply this to the house price prediction API we worked on in the last lesson. We'll add a rule: our API only supports specific model versions, "v1" and "v2". If a client requests any other version, we should return an error.

Task:

  1. In your main.py file, locate the predict_house_price endpoint.
  2. Inside the function, add a check for the model_version query parameter.
  3. If model_version is provided (i.e., not None) and its value is not in ["v1", "v2"], you should raise HTTPException.
  4. The HTTPException should have a status_code of 404 and a detail message like f"Model version '{model_version}' not found. Available versions: v1, v2".
  5. Make sure you have HTTPException imported from fastapi.
  6. Run your app with Uvicorn and test this in the Swagger UI.
    • Try making a request with model_version set to v1 (should succeed).
    • Try making a request with model_version set to v3 (should fail with a 404 error and your custom message).
Click here for the solution



# In main.py
# Make sure HTTPException is imported at the top
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field




# ... (keep your existing app and Pydantic models)
app = FastAPI()

class HousePredictionInput(BaseModel):
  area_sqft: float = Field(..., gt=0, description="Area of the house in square feet")
  num_bedrooms: int = Field(..., gt=0, description="Number of bedrooms")
  has_garage: bool

class HousePredictionOutput(BaseModel):
  predicted_price: float
  client_id: str
  model_version_used: str | None


@app.post(
  "/predict/{client_id}/house-price", 
  response_model=HousePredictionOutput
)
async def predict_house_price(
  client_id: str,
  house_data: HousePredictionInput,
  model_version: str | None = "v1" # Let's default to v1
):



  # New validation logic for model_version
  available_models = ["v1", "v2"]
  if model_version not in available_models:
      raise HTTPException(
          status_code=404,
          detail=f"Model version '{model_version}' not found. Available versions: {available_models}"
      )




  # Dummy prediction logic (same as before)
  price_factor = 150
  if model_version == "v2":
      price_factor = 175

  predicted_price = 150_000 + (house_data.area_sqft * price_factor) + (house_data.num_bedrooms * 20_000)
  if house_data.has_garage:
      predicted_price += 25_000
  
  return {
      "predicted_price": predicted_price,
      "client_id": client_id,
      "model_version_used": model_version
  }

Conclusion

Excellent work! You now have a professional and robust way to handle business logic errors in your FastAPI applications. Communicating failures clearly is just as important as handling successes, and HTTPException is the primary tool for this job.

Key Takeaways:

  • Standard API practice requires using appropriate HTTP status codes to signal errors (e.g., 4xx range for client errors).
  • Use raise HTTPException to stop request processing and immediately return a custom error response.
  • The status_code and detail parameters of HTTPException allow you to control the response sent to the client.
  • This approach is for handling errors in your application's logic, distinct from the automatic validation errors FastAPI provides for data shapes.

In the next lesson, we will shift our focus to project organization. As you add more endpoints, your main.py file can become crowded. We'll learn how to use APIRouter to split your endpoints into multiple files, making your application more modular and maintainable.