Skip to content

Exceptions & Status Codes

The exception hierarchy, the built-in handlers, and the status-code constants.

Every exception below inherits VeloceError, so except VeloceError catches anything the framework raised regardless of which family it came from.

VeloceError

Bases: Exception

Root of every exception Veloce raises.

Mixed into each exception family the framework defines - HTTP errors, validation failures, WebSocket closes, routing and setup errors, JWT and signature failures - so except VeloceError answers "did this come from Veloce?" in one clause. Families that already subclassed a stdlib type keep it: DuplicateRouteError is still a ValueError and FilesKeyError is still a KeyError, so every handler that matched before still matches. VeloceError is listed first in those bases, so a handler registered against it wins the MRO walk over a broader stdlib handler.

HTTPException

Bases: VeloceError

HTTP error with status code and detail.

Either subclass with a fixed code (and optional description), or instantiate HTTPException(status_code, detail, headers) directly.

ValidationError

Bases: UnprocessableEntity

Request validation error (422).

Subclasses UnprocessableEntity so handlers registered against either UnprocessableEntity or HTTPException catch it via the MRO walk Veloce performs in error dispatch.

RequestValidationError

Bases: ValidationError

Framework-level request validation failure (422).

Raised by the dependency resolver when path / query / header / cookie / body / form / file parameters fail validation. Distinct from a user-level ValidationError so handlers can pick one or the other:

@app.exception_handler(RequestValidationError)
async def on_req_invalid(request, exc):
    return JSONResponse(
        {"errors": exc.errors},
        status_code=HTTP_422_UNPROCESSABLE_ENTITY,
    )

Subclasses ValidationError so existing except ValidationError handlers continue to catch it via the MRO walk.

WebSocketException

Bases: VeloceError

Raised inside a WebSocket handler to close the connection cleanly.

ASGI shape. The dispatch layer catches it and sends a close frame carrying code (RFC 6455 Sec. 7.4.1) and the optional reason - no traceback is propagated, since this is an application-driven close rather than an internal error.

WebSocketDisconnect

Bases: VeloceError

WebSocket connection closed.

WebSocketRequestValidationError

Bases: RequestValidationError

A WebSocket dependency failed parameter validation.

Raised when a Depends() resolved during a WebSocket handshake reports a RequestValidationError. The dispatch layer closes the connection with code 1008 (policy violation) rather than 1011 (internal error), since the failure is a client-side contract violation, not a server fault.

BuildError

Bases: VeloceError, LookupError

url_for could not build a URL for the given endpoint.

Carries the endpoint name and the values that were being substituted so registered app.url_build_error_handlers callbacks can recover (e.g. fall back to a different endpoint, or fetch from an external routing table) by inspecting the failure and returning a URL string.

ConfigurationError

Bases: VeloceError, RuntimeError

A handler or route was declared in a way that cannot be resolved.

Raised at registration time, never per request, so a genuinely ambiguous parameter declaration becomes a startup error instead of a silent mis-binding discovered only at runtime. Carries the offending parameter name so the message points straight at the conflict.

DuplicateRouteError

Bases: VeloceError, ValueError

Two handlers were registered for the same path and HTTP method.

Raised at registration time when a route would silently overwrite an existing handler. Carries the conflicting path, method, and both handler qualified names so the message points at the exact collision. Configure the policy per router with on_duplicate="error"|"warn"|"override".

FilesKeyError

Bases: VeloceError, KeyError

Descriptive miss on request.files raised in debug mode.

Subclasses KeyError so handlers that already catch the bare lookup miss keep working, while the message explains the most common cause: the field was submitted as a plain form value (missing enctype="multipart/form-data") or the body was JSON rather than a multipart upload. Only raised when app.debug is set; production keeps the plain KeyError semantics.

SetupError

Bases: VeloceError, RuntimeError

A registration ran after the application started serving.

Routes, hooks, blueprints, middleware, and similar setup must be wired before the first request is dispatched. Once serving begins the route table and hook lists are frozen, so a late mutation - which would race in-flight requests under concurrent ASGI dispatch - raises this instead of silently corrupting the live application. The lock is relaxed in DEBUG/TESTING so hot-reload and test monkeypatching stay ergonomic.

http_exception_handler async

http_exception_handler(request: Any, exc: HTTPException) -> Response

Render an HTTPException as a JSON {"detail": ..., "status_code": ...} response.

Honours exc.status_code, exc.detail (falling back to the subclass description), and exc.headers.

request_validation_exception_handler async

request_validation_exception_handler(request: Any, exc: RequestValidationError) -> Response

Render a RequestValidationError as a 422 with the error list.

Uses the structured shape {"detail": [ ...per-field errors... ]}.

status

HTTP status codes — convenient named constants.

Usage::

from veloce import status

@app.post("/items", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
    return item

status_permits_body

status_permits_body(code: int | None) -> bool

Whether a response with this status may carry a payload body.

False for 1xx interim responses (RFC 9110 Sec. 15.2), 204 No Content (Sec. 15.3.5), 205 Reset Content (Sec. 15.3.6), and 304 Not Modified (Sec. 15.4.5); True otherwise. None (unknown) allows a body.

Named HTTP errors

One class per standard status code. Each carries a fixed code and description, so raise NotFound("no such item") produces a 404 whose body defaults to "Not Found". abort(404) raises the same class, which is why a handler registered against NotFound matches an abort() too.

ServerNotImplemented is the 501 class: NotImplemented is a Python builtin, so the obvious name is unavailable.

BadRequest

Bases: HTTPException

Unauthorized

Bases: HTTPException

PaymentRequired

Bases: HTTPException

Forbidden

Bases: HTTPException

NotFound

Bases: HTTPException

MethodNotAllowed

Bases: HTTPException

NotAcceptable

Bases: HTTPException

ProxyAuthenticationRequired

Bases: HTTPException

RequestTimeout

Bases: HTTPException

Conflict

Bases: HTTPException

Gone

Bases: HTTPException

LengthRequired

Bases: HTTPException

PreconditionFailed

Bases: HTTPException

RequestEntityTooLarge

Bases: HTTPException

RequestURITooLong

Bases: HTTPException

UnsupportedMediaType

Bases: HTTPException

RangeNotSatisfiable

Bases: HTTPException

ExpectationFailed

Bases: HTTPException

ImATeapot

Bases: HTTPException

UnprocessableEntity

Bases: HTTPException

TooManyRequests

Bases: HTTPException

InternalServerError

Bases: HTTPException

ServerNotImplemented

Bases: HTTPException

BadGateway

Bases: HTTPException

ServiceUnavailable

Bases: HTTPException

GatewayTimeout

Bases: HTTPException