Error Handling

flarchitect standardises error reporting through a small set of helpers. These utilities ensure your API returns consistent payloads regardless of where an exception originates.

CustomHTTPException

CustomHTTPException is a lightweight wrapper around an HTTP status code and optional reason. Raise it in your views when you need to abort a request with a specific status:

from flarchitect.exceptions import CustomHTTPException

@app.get("/widgets/<int:id>")
def get_widget(id: int):
    widget = Widget.query.get(id)
    if widget is None:
        raise CustomHTTPException(404, "Widget not found")
    return widget

The exception exposes a to_dict method which yields a structured payload containing the status_code, status_text and reason fields.

handle_http_exception

Register handle_http_exception as the Flask error handler for CustomHTTPException to automatically serialise the exception into a create_response payload:

from flarchitect.exceptions import CustomHTTPException, handle_http_exception

app.register_error_handler(CustomHTTPException, handle_http_exception)

A 404 from the example above produces the following JSON response:

{
  "api_version": "0.1.0",
  "datetime": "2024-01-01T00:00:00+00:00",
  "status_code": 404,
  "errors": {"error": "Not Found", "reason": "Widget not found"},
  "response_ms": 5.0,
  "total_count": 1,
  "next_url": null,
  "previous_url": null,
  "value": null
}

Using _handle_exception

For ad-hoc exception handling you can call _handle_exception directly. It accepts an error string, HTTP status code and optional reason, returning the same structured response used throughout the library:

from flarchitect.exceptions import _handle_exception

@app.get("/divide")
def divide() -> Response:
    try:
        result = expensive_division()
    except ZeroDivisionError as exc:
        return _handle_exception("Bad Request", 400, str(exc))
    return {"value": result}

This helper is useful when catching non-Flask exceptions but still wanting a uniform error format.

Common status codes

flarchitect normalises a consistent set of HTTP statuses across endpoints:

  • 400 Bad Request: validation errors (Marshmallow deserialisation), invalid query parameters, malformed inputs (e.g., missing refresh token), and SQL formatting issues.

  • 401 Unauthorized: missing/invalid Authorization header, invalid JWT (bad signature/claims), unauthenticated access to protected routes.

  • 403 Forbidden: insufficient roles or permissions, invalid/revoked/expired-in-store refresh tokens.

  • 404 Not Found: resource lookup by id or relationship yields no results; user not found during token refresh.

  • 409 Conflict: delete operations blocked by related records or cascade rules.

  • 422 Unprocessable Entity: database integrity or data type errors on create/update (e.g., uniqueness violations).

  • 429 Too Many Requests: rate limit exceeded when API_RATE_LIMIT is configured (headers include standard rate-limit fields).

  • 405 Method Not Allowed: Flask-level response when an endpoint does not support the HTTP method; serialised by the default error handler for API routes.

  • 500 Internal Server Error: uncaught exceptions or misconfiguration (e.g., missing JWT keys, soft delete misconfiguration).