Skip to content

Errors

mongospecs raises its own exceptions, all subclasses of MongoSpecsError and all importable from mongospecs. When one wraps a driver error, that error is its __cause__.

from mongospecs import DuplicateKeyError

try:
    await User(email="ann@example.com").insert()
except DuplicateKeyError as exc:
    print(exc.fields)        # {"email": "ann@example.com"}

What each one means

The last column is what a mongospecs-web API answers when the error reaches it. Anything not listed becomes a 500 with no detail, and the cause is logged.

Error Raised when What to do HTTP
ValidationError a value does not fit the model: on read, on any write, in a request body. .errors lists {"field", "message"} fix the input; for stored documents see SpecConfig(upcast=...) and find().on_invalid(...) 422
QueryError a filter, sort or update names an unknown field, has a value that cannot be cast, or a raw dict contains an operator fix the query; wrap filters you wrote yourself in trusted() 400
DocumentNotFound one(), or_fail(), update(), save() or delete() matched nothing treat as "gone" 404
StaleDocumentError a Revision check failed: the document changed since you loaded it reload and retry, or tell the user 412
DuplicateKeyError a unique index was violated. .fields maps index keys to the offending values report the conflict 409
EncodingError a value has no BSON form (an integer beyond 64 bits, an unsupported type) store a type MongoDB has 422
NotLoadedError ref.doc was read before the reference was loaded add .include("field") or await ref.fetch() 500
OperationTimeout the operation passed its time limit (max_time_ms) add an index, raise the limit, or retry 504
PoolTimeout no pooled connection became free in time the server is overloaded; see Tuning for production 503
ConnectionFailed the server could not be reached retry later 503
TransactionError a transaction could not be committed, or transaction() was called inside another pass the outer tx down 500
DocumentTooLarge a document or an aggregation result passed 16 MB split the document 500
BulkWriteFailed part of a bulk write failed. .write_errors lists the items by input index handle per item 500
OperationFailed the server rejected the operation for another reason. .code is the server's error code depends on the code 500
ConfigurationError a model, plugin or registry is declared incorrectly; raised when the model is first used fix the declaration at startup
NotBoundError a model was used before it was given to a registry add it to Registry(models=[...]) at startup
RegistryClosedError an operation ran on a registry that is not open open it first; a closed registry cannot be reopened 500

QueryError, ValidationError and EncodingError are also ValueErrors, and DocumentNotFound is a LookupError, so code that already catches those keeps working.

Batch operations that partly succeed (bulk_save) raise after writing what they could; the documents that were not written are in the error's .failed.

Errors of your own, over HTTP

In a guard, hook or custom operation, raise one of the errors from mongospecs_web to choose the status yourself: BadRequest, Unauthorized, Forbidden, NotFound, Conflict, PreconditionFailed, Unprocessable, or HTTPError(detail, status=..., title=...).

from mongospecs_web import Conflict

@orders.op("ship", method="POST")
async def ship(ctx):
    order = await ctx.load()
    if order.shipped:
        raise Conflict("This order has already shipped.")
    ...

Every error response is an RFC 9457 application/problem+json document:

{"type": "about:blank", "title": "Unprocessable Content", "status": 422,
 "detail": "The request body failed validation.",
 "errors": [{"field": "title", "message": "Expected `str`, got `int`"}]}