Skip to content

Exceptions & Exception Handlers

Exception handlers are, as the name suggests, the handlers in case an exception of type X occurs.

Exception handlers

In every level the exception_handler parameter (among others) are available to be used and handle specific exeptions raised on each level.

The exception handlers are read a python dictionary and you can pass the key as the exception itself or the status_code that will always use the exception itself.

from json import loads

from lilya import status
from lilya.apps import Lilya
from lilya.requests import Request
from lilya.responses import JSONResponse
from lilya.routing import Include, Path


async def handle_type_error(request: Request, exc: TypeError):
    status_code = status.HTTP_400_BAD_REQUEST
    details = loads(exc.json()) if hasattr(exc, "json") else exc.args[0]
    return JSONResponse({"detail": details}, status_code=status_code)


async def handle_value_error(request: Request, exc: ValueError):
    status_code = status.HTTP_400_BAD_REQUEST
    details = loads(exc.json()) if hasattr(exc, "json") else exc.args[0]
    return JSONResponse({"detail": details}, status_code=status_code)


async def me():
    return "Hello, world!"


app = Lilya(
    routes=[
        Include(
            "/",
            routes=[
                Path(
                    "/me",
                    handler=me,
                )
            ],
        )
    ],
    exception_handlers={
        TypeError: handle_type_error,
        ValueError: handle_value_error,
    },
)

What is happening

The application level contains an exception handler handle_type_error and the handle_value_error and that means that for every HTTPException and ValueError being raised in the application it will be handled by that function.

Custom exception handlers

We all know that Lilya handles really well the exceptions by design but sometimes we might also want to throw an error while doing some code logic that is not directly related with a data of an handler.

Example

from lilya.apps import Lilya
from lilya.requests import Request
from lilya.routing import Path


async def create(request: Request):
    data = await request.json()
    if len(data) == 0:
        raise ValueError("Cannot be 0.")


app = Lilya(
    routes=[
        Path(
            "/create",
            handler=create,
            methods=["POST"],
        )
    ],
)

This example is a not usual at all but it serves to show where an exception is raised.

Lilya offers one out of the box custom exception handlers:

  • handle_value_error - When you want the ValueError exception to be automatically parsed into a JSON.
from lilya._internal._exception_handlers import handle_value_error

How it would look like the previous example using this custom exception handler?

from lilya._internal._exception_handlers import handle_value_error
from lilya.apps import Lilya
from lilya.requests import Request
from lilya.routing import Path


async def create(request: Request):
    data = await request.json()
    if len(data) == 0:
        raise ValueError("Cannot be 0.")


app = Lilya(
    routes=[Path("/create", handler=create)],
    exception_handlers={
        ValueError: handle_value_error,
    },
)

Using status codes

When declaring exception handlers, as mentioned before, you can use status codes instead of the exception itself. This allows and indicates how an exception must be handle when a specific status_code occurs.

This can be very useful if you only want to narrow down to status_code approach instead of the whole Exception itself.

from lilya.apps import Lilya
from lilya.exceptions import HTTPException
from lilya.requests import Request
from lilya.responses import Error


async def not_found(request: Request, exc: HTTPException):
    return Error("Oops", status_code=exc.status_code)


async def server_error(request: Request, exc: HTTPException):
    return Error("Oops", status_code=exc.status_code)


app = Lilya(
    routes=...,
    exception_handlers={
        404: not_found,
        500: server_error,
    },
)

HTTPException

The HTTPException class serves as a foundational class suitable for handling various exceptions. In the default ExceptionMiddleware implementation, plain-text HTTP responses are returned for any instance of HTTPException.

Note

The proper usage dictates that you exclusively raise HTTPException within routing or endpoints. Middleware and Permission classes, on the other hand, should simply return the appropriate responses directly.

WebSocketException

The WebSocketException class is designed for raising errors specifically within WebSocket endpoints.