Every failure raises a subclass of usearch.Error, itself a RuntimeError, and each carries the status the C ABI reported as its status attribute, a usearch.Status member:
try: index.on(pool).search(queries, 10, arena, progress=lambda completed, total: completed < 100)except usearch.CancelledError: ... # The handler stopped the call, which published nothingexcept usearch.Error as error: print(error.status) # Status.SHAPE_MISMATCH, Status.READ_ONLY, Status.DEVICE, ...
usearch.Status is an enum.IntEnum valued as usearch_status_t. SUCCESS and DEFERRED raise nothing, and every other member has its own class, which also derives the built-in naming the same fault where one exists:
OUT_OF_MEMORY → OutOfMemoryError, a MemoryError
SHAPE_MISMATCH → ShapeMismatchError, a ValueError
UNSUPPORTED_LAYOUT → UnsupportedLayoutError, a ValueError
UNKNOWN_KIND → UnknownKindError, a ValueError
READ_ONLY → ReadOnlyError
IO → InputOutputError, an OSError
DEVICE → DeviceError
MALFORMED → MalformedError, a ValueError
INTERNAL → InternalError
OVERFLOW → CapacityOverflowError, an OverflowError
UNINITIALIZED → UninitializedError
CONFIG_MISMATCH → ConfigMismatchError, a ValueError
CONTENDED → ContendedError
CANCELLED → CancelledError
UNSUPPORTED → UnsupportedError, a NotImplementedError
INVALID_ARGUMENT → InvalidArgumentError, a ValueError
So except ValueError catches a ShapeMismatchError, except OSError an InputOutputError, and except MemoryError an OutOfMemoryError.
A failure the binding catches before the boundary raises the class the engine would have raised for the same fault.
Arguments the binding refuses raise at the call, while the engine’s refusals of a deferred call raise at await or wait(), as many times as they are asked.