DEV Community

Cover image for Day 10: Exception Handling & Custom Errors in FastAPI
Utkarsh Rastogi
Utkarsh Rastogi

Posted on • Edited on

Day 10: Exception Handling & Custom Errors in FastAPI

Welcome to Day 10 of the FastAPI Zero to Hero πŸš€ series!

Today, we dive into something every developer faces β€” Errors! But instead of fearing them, let's learn how to handle them like a pro in FastAPI. πŸ’ͺ


🎯 Why Exception Handling Matters

Think about this:

Online, you're placing a food order. The pincode you entered is not valid.

The application should gently notify you that the pincode is invalid or crash. Please give it another go."_?

It is normal to make mistakes in the real world. Effective APIs communicate what went wrong; they don't break.

Because of this, exception handling is essential for: - A pristine user experience
Improved debugging; safe and dependable APIs


βš™οΈ What You'll Learn Today

  • Using HTTPException (built-in FastAPI tool)
  • Creating global exception handlers
  • Defining your own custom exceptions like a boss πŸ’Ό

πŸ’₯ 1. Raise an HTTPException

When you want to throw a standard API error, use HTTPException.

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id: int):
    if item_id == 0:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item_id": item_id}
Enter fullscreen mode Exit fullscreen mode

🌐 2. Global Exception Handler

Want to catch all ValueErrors and return a clean message instead of a scary stack trace?

Let’s register a global exception handler in FastAPI πŸ‘‡

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

# Global exception handler for ValueError
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
    return JSONResponse(
        status_code=400,
        content={"detail": str(exc)}
    )

Enter fullscreen mode Exit fullscreen mode

βœ… What comes next?

Instead of displaying the raw traceback, FastAPI will automatically utilise this handler whenever a ValueError is raised in your application and return a tidy JSON error.


πŸ§‘β€πŸŽ¨ 3. Create Your Own Custom Exceptions

Built-in exceptions such as HTTPException and ValueError are insufficient in certain situations.

What if you require a bespoke PaymentFailedError for your business logic?

You can create your own exception classes using FastAPI and manage them effectively with unique replies and logic.


🧱 Step 1: Define Your Custom Exception

class PaymentFailedError(Exception):
    def __init__(self, reason: str):
        self.reason = reason

Enter fullscreen mode Exit fullscreen mode

This is just a normal Python exception. You can extend it with more attributes if needed.


πŸ”§ Step 2: Register a Custom Exception Handler

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(PaymentFailedError)
async def payment_failed_handler(request: Request, exc: PaymentFailedError):
    return JSONResponse(
        status_code=402,
        content={"detail": f"Payment failed: {exc.reason}"}
    )
Enter fullscreen mode Exit fullscreen mode

Here, whenever PaymentFailedError is raised, FastAPI will use this handler to return a structured JSON response.


πŸš€ Step 3: Use Your Custom Exception in a Route

@app.get("/pay")
def pay():
    # Simulate a failed payment scenario
    raise PaymentFailedError("Card declined by bank")
Enter fullscreen mode Exit fullscreen mode

βœ… Full FastAPI Code Example with Exception Handling

This example covers:

  • πŸ”Ή Raising built-in HTTPException
  • 🌐 Global handler for ValueError
  • πŸ§‘β€πŸŽ¨ Custom exception: PaymentFailedError
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse

app = FastAPI()

# -----------------------------------
# 1. Raise HTTPException Example
# -----------------------------------
@app.get("/items/{item_id}")
def read_item(item_id: int):
    if item_id == 0:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item_id": item_id}

# -------------------------------------------
# 2. Global Exception Handler for ValueError
# -------------------------------------------
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
    return JSONResponse(
        status_code=400,
        content={"detail": f"ValueError: {str(exc)}"},
    )

@app.get("/divide")
def divide_numbers(a: int = 10, b: int = 0):
    if b == 0:
        raise ValueError("Division by zero is not allowed.")
    return {"result": a / b}

# -----------------------------------
# 3. Custom Exception: PaymentFailed
# -----------------------------------
class PaymentFailedError(Exception):
    def __init__(self, reason: str):
        self.reason = reason

@app.exception_handler(PaymentFailedError)
async def payment_failed_handler(request: Request, exc: PaymentFailedError):
    return JSONResponse(
        status_code=402,
        content={"detail": f"Payment failed: {exc.reason}"},
    )

@app.get("/pay")
def pay():
    raise PaymentFailedError("Card declined by bank")

Enter fullscreen mode Exit fullscreen mode

▢️ Run the FastAPI App

Use uvicorn to run your FastAPI server locally:

uvicorn excpetions:app --host 0.0.0.0 --reload --port 9000
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή /items/0 β†’ HTTPException

URL to hit:
http://localhost:9000/items/0

Items


🌐 /divide?a=10&b=0 β†’ Global ValueError Handler

URL to hit:
http://localhost:9000/divide?a=10&b=0

Divide


πŸ§‘β€πŸŽ¨ /pay β†’ Custom PaymentFailedError

URL to hit:
http://localhost:9000/pay

Pay


🧾 Wrap-Up: Why Exception Handling Matters

Exception handling isn't just a developer convenience β€” it's a critical part of building reliable APIs.

In this FastAPI example, you learned how to:

βœ… Raise built-in exceptions like HTTPException for standard error cases

🌐 Register global handlers (e.g., for ValueError) to keep your code DRY and consistent

πŸ§‘β€πŸŽ¨ Create custom exceptions like PaymentFailedError to match real-world business scenarios


πŸ” Real-World Relevance

Here’s how this maps to real-world applications:

  • HTTPException: Missing resources like /user/0, /product/9999 β†’ 404 Not Found
  • ValueError: Invalid input like dividing by zero or parsing errors β†’ 400 Bad Request
  • Custom Exceptions: Payment failures, quota limits, licensing errors β†’ Business-specific error codes like 402 Payment Required, 429 Too Many Requests, etc.

πŸš€ Pro Tip

With well-structured exception handling:

  • Your API becomes more developer-friendly
  • Your errors are easier to log, debug, and trace
  • You leave room to plug in monitoring tools, custom logging, or even alerting systems

πŸ™ Credits

Huge thanks to the FastAPI Official Documentation by SebastiΓ‘n RamΓ­rez (@tiangolo) β€” the best place to learn and explore everything about FastAPI.


πŸ‘¨β€πŸ’» About Me

Hey there! I’m Utkarsh Rastogi, an AWS Community Builder and passionate cloud-native enthusiast who loves building scalable backend systems and sharing knowledge with the community.

πŸ”— Connect with me: Utkarsh Rastogi


πŸ’¬ Share Your Thoughts – I'd Love Your Feedback!

If you enjoyed today's post or learned something new, I'd truly appreciate it if you leave a comment or share your thoughts πŸ‘‡

Your feedback, questions, or even a quick β€œπŸ”₯ Loved this!” keeps me motivated to continue this journey and share more in the upcoming #FastAPIDaily posts.

βœ… What did you find most helpful?

βœ… Anything you'd like explained in the next part?

βœ… Suggestions for improvement? I’m all ears! πŸ™Œ

Let’s grow and learn together β€” one FastAPI day at a time πŸš€


Top comments (0)