Skip to content
errium

Django Ninja

Overriding Ninja's defaults, and the parameter-name quirk.

pip install "errium[ninja]"

Integration

api.py
from ninja import NinjaAPI
from errium_ninja import register_errium

api = NinjaAPI()
register_errium(api)

register_errium(api) also accepts classifiers=[...] and engine= — see custom classifiers.

It registers one shared handler against four exception types via api.exception_handler(...):

  • ninja.errors.ValidationError
  • ninja.errors.HttpError — including its AuthenticationError, AuthorizationError and Throttled subclasses
  • django.http.Http404
  • the base Exception

Why it overrides Ninja’s defaults

Ninja installs its own handlers for all four of those types, and registering Errium deliberately replaces them.

The parameter-name quirk

Ninja wraps a single Schema-typed body or form parameter behind a synthetic field named after the endpoint’s own parameter. For this endpoint:

@api.post("/users")
def create_user(request, payload: UserCreate):
    ...

a missing password arrives with loc of ("body", "payload", "password") — where payload is the Python parameter name, not anything the client sent.

_flatten_ninja_errors() drops that segment, so details comes back keyed on password exactly as it would from FastAPI or Flask, rather than leaking an internal name to your API consumers. Query, path and header params don’t get the wrapper; Ninja already flattens those.

Classifiers

ClassifierPriorityHandles
NinjaResponseValidationClassifier215A bare pydantic ValidationError → 500
NinjaValidationErrorClassifier210Ninja’s ValidationError → 422
NinjaHttpErrorClassifier200HttpError and subclasses
DjangoHttp404Classifier200Http404, e.g. from get_object_or_404
DjangoExceptionClassifier190Django’s own exceptions (below)

Django’s own exceptions

Overriding Ninja’s Exception handler would otherwise discard Django’s usual mapping and turn everything into a 500. DjangoExceptionClassifier restores it:

Raised in a viewResponse
PermissionDenied403 AUTHORIZATION_ERROR
SuspiciousOperation400 BAD_REQUEST
RequestDataTooBig, TooManyFieldsSent413 PAYLOAD_TOO_LARGE
ObjectDoesNotExist (and Model.DoesNotExist)404 RESOURCE_NOT_FOUND
django.core.exceptions.ValidationError422 VALIDATION_ERROR, with details
django.db.IntegrityError (unique)409 DUPLICATE_RESOURCE
other django.db errors500 (DATABASE_ERROR in debug)

Response-schema mismatches are a 500

When an endpoint declares response= and returns data that does not match, Ninja raises a bare pydantic ValidationError while serializing. That is a bug in your server, not in the request, so Errium reports it as a 500 instead of a 422 that would blame the client. Request validation (ninja.errors.ValidationError) is still 422.

Errors outside your views

An unknown URL, a URL-converter mismatch, and Ninja’s own 405 never reach these handlers; Django renders them as HTML. The Django helpers cover them.