DEV Community

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

Posted on β€’ Edited on

2

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 πŸš€


Tiger Data image

🐯 πŸš€ Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

Top comments (0)

Build gen AI apps that run anywhere with MongoDB Atlas

Build gen AI apps that run anywhere with MongoDB Atlas

MongoDB Atlas bundles vector search and a flexible document model so developers can build, scale, and run gen AI apps without juggling multiple databases. From LLM to semantic search, Atlas streamlines AI architecture. Start free today.

Start Free

πŸ‘‹ Kindness is contagious

Explore this insightful piece, celebrated by the caring DEV Community. Programmers from all walks of life are invited to contribute and expand our shared wisdom.

A simple "thank you" can make someone’s dayβ€”leave your kudos in the comments below!

On DEV, spreading knowledge paves the way and fortifies our camaraderie. Found this helpful? A brief note of appreciation to the author truly matters.

Let’s Go!