Faults — Aquilia Documentation
Comprehensive guide and documentation for Faults in the Aquilia framework. View API reference, examples, and implementation patterns.
Contracts / Faults Contract Faults Contracts use structured fault types instead of bare exceptions. All Contract errors are part of the CONTRACT fault domain and integrate seamlessly with the AquilaFaults error handling system. Fault Taxonomy , , , , , , , , , , , ].map((item, i) => ( ))} CastFault Raised when a Facet cannot coerce a raw value to the expected Python type: from aquilia.contracts.exceptions import CastFault # CastFault is raised internally during Phase 1 (Cast) # Typically caught by the Contract and added to errors dict # Example triggers: # IntFacet receives "not a number" # DateTimeFacet receives "invalid date" # BoolFacet receives [1, 2, 3] # EmailFacet receives "not@an@email" try: facet = IntFacet() facet.cast("hello") except CastFault as e: print(e.field) # "quantity" print(e.message) # "Expected integer, got str" print(e.code) # "BP100" Definition-Time CastFaults Some facet constraints are validated while the Contract class is created. These errors are programming or security errors and must be raised immediately, including when Python 3.14 evaluates a deferred annotation inside the metaclass. import pytest from aquilia.contracts import Contract, Field from aquilia.contracts.exceptions import CastFault with pytest.raises(CastFault): class UnsafeSearch(Contract): # Nested quantifiers are rejected as a ReDoS risk at definition time. query: str = Field(pattern=r"(a+)+") Before v1.4.0b5 on Python 3.14, the metaclass could swallow this fault as a generic annotation introspection failure and create an incompletely validated Contract. The fault now propagates; no migration is required unless tests incorrectly expected the unsafe class to be created. SealFault Raised when validation fails — either from Facet constraints or from custom seal methods: from aquilia.contracts.exceptions import SealFault bp = ProductContract(data= ) # SealFault accumulates all errors bp.is_sealed() print(bp.errors) # # With raise_fault=True: try: bp.is_sealed(raise_fault=True) except SealFault as e: print(e.field_errors) # dict of field → error messages print(e.error_count) # Total number of errors print(e.as_response_body()) # # } Handling Contract Faults from aquilia import Controller, Post from aquilia.faults import fault_handler from aquilia.contracts.exceptions import SealFault class ProductController(Controller): prefix = "/api/products" @Post("/", status_code=201) async def create(self, ctx, payload: ProductContract): # Pattern 1: Check and return errors if not payload.is_sealed(): return ctx.json(payload.errors, status=422) product = payload.imprint() await product.save() return ctx.json(ProductContract(instance=product).data, status=201) # Pattern 2: Global fault handler for SealFault @fault_handler(SealFault) async def handle_seal_fault(ctx, fault): """Auto-converts SealFault to 422 response.""" return ctx.json(fault.as_response_body(), status=422) # Register in workspace: # workspace = Workspace(fault_handlers=[handle_seal_fault]) # Pattern 3: raise_fault=True for exception-based flow class StrictProductController(Controller): prefix = "/api/strict/products" @Post("/") async def create(self, ctx, payload: ProductContract): # This raises SealFault if validation fails # The global fault handler catches it automatically payload.is_sealed(raise_fault=True) product = payload.imprint() await product.save() return ctx.json(ProductContract(instance=product).data, status=201) Integration with AquilaFaults All Contract faults inherit from Fault and integrate with Aquilia's fault engine: from aquilia.faults.core import Fault, Severity, FaultDomain from aquilia.contracts.exceptions import SealFault, CastFault # All Contract faults have: fault = SealFault(field="email", message="Invalid email") fault.code # "BP200" fault.severity # Severity.WARN fault.domain # FaultDomain.CONTRACT (or VALIDATION) fault.public # True (safe to show to user) fault.retryable # False (fix the input, don't retry) # They appear in the FaultEngine's error log # They are caught by global fault handlers # They integrate with the trace/observability system )
Go to Homepage