All Python exceptions raised by ELIPS derive from elips.ElipsError. The C++ engine exceptions are mapped cleanly to idiomatic Python exception classes.
Overview
ELIPS uses specific exception types so you can catch precisely and build targeted retry logic — for cross-process lock contention, mismatched vector dimensions, malformed vectors, missing collections or ids, untrained quantizers, absent compute backends, bad EQL, and storage I/O failures.
Exception Hierarchy
ElipsError # base class for every ELIPS error
├── ConfigError # invalid or conflicting configuration
├── DimensionMismatch # vector dimension != collection dimension
│ # (alias: DimensionMismatchError)
├── InvalidVector # malformed vector (NaN/inf, wrong element type)
│ # (alias: InvalidVectorError)
├── LockConflict # cross-process write lock could not be acquired
│ # (alias: LockConflictError)
├── NotFound # id missing on update()/get()
│ # (alias: NotFoundError)
├── CollectionNotFoundError # get_collection() on a collection that does not exist
├── NotTrainedError # query/compress before the codebook was trained
├── BackendUnavailableError # a named compute backend (e.g. "cuda") is absent
├── ParseError # malformed EQL statement
└── StorageError # WAL / snapshot / disk I/O failureConfigError
Raised when a configuration value is invalid, or when a persisted identity field (dimension, metric, index type) conflicts with the value already stored on disk.
import elips
# Opening an existing database with a mismatched dimension
try:
client = elips.PersistentClient("/data/v", dimension=512) # on-disk value is 768
except elips.ConfigError as e:
print(f"Config conflict: {e}")DimensionMismatch
Raised when a vector whose length does not match the collection's configured dimension is added or used as a query. Also exported as DimensionMismatchError.
with elips.Client(dimension=128) as client:
docs = client.get_or_create_collection("vectors")
try:
# 3-dimensional vector added to a 128-dim collection
docs.add(vector=[1.0, 2.0, 3.0])
except elips.DimensionMismatch as e:
print(f"Dimension mismatch: {e}")InvalidVector
Raised when a vector is structurally malformed — for example it contains NaN/inf values or non-numeric elements. Also exported as InvalidVectorError.
with elips.Client(dimension=2) as client:
docs = client.get_or_create_collection("vectors")
try:
docs.add(vector=[float("nan"), 1.0])
except elips.InvalidVector as e:
print(f"Invalid vector: {e}")LockConflict
Raised when another OS process holds an exclusive write lock on the database directory and a read_write handle cannot be acquired. Also exported as LockConflictError.
import elips
import time
def open_with_retry(db_path: str, max_retries: int = 5):
for attempt in range(max_retries):
try:
return elips.PersistentClient(db_path, dimension=128)
except elips.LockConflict:
print(f"Locked by another process; retrying ({attempt + 1}/{max_retries})...")
time.sleep(1.0)
raise RuntimeError("Failed to acquire database lock.")NotFound & CollectionNotFoundError
NotFound(aliasNotFoundError) — raised bycollection.update()(and other id-targeted operations) when the record id does not exist.CollectionNotFoundError— raised byclient.get_collection(name)when the named collection does not exist. Useget_or_create_collection()if you want lazy creation instead.
with elips.Client(dimension=2) as client:
try:
client.get_collection("missing")
except elips.CollectionNotFoundError as e:
print(f"No such collection: {e}")
docs = client.get_or_create_collection("docs")
try:
docs.update(ids=["does-not-exist"], metadatas=[{"k": 1}])
except elips.NotFound as e:
print(f"No such record: {e}")NotTrainedError
Raised when an operation requires a trained quantization codebook that has not been built yet — for example querying a quantized collection, or calling collection.compress() before train has run.
try:
docs.compress() # requires a trained codebook
except elips.NotTrainedError as e:
print(f"Codebook not trained: {e}")BackendUnavailableError
Raised when a named compute backend is requested but is not present in the current build or on the current hardware — for example opening with device="cuda:0" on a machine with no CUDA device. Pass fallback=True to fall back to CPU instead of raising.
try:
client = elips.Client(dimension=128, device="cuda:0")
except elips.BackendUnavailableError as e:
print(f"Backend unavailable: {e}")
# Or request graceful fallback to CPU
client = elips.Client(dimension=128, device="cuda:0", fallback=True)ParseError
Raised when an EQL statement is syntactically invalid. Surfaced by client.query_eql() and the standalone elips.parse_eql() / elips.validate_eql() helpers.
try:
client.query_eql("search in docs nearest") # incomplete statement
except elips.ParseError as e:
print(f"Bad EQL: {e}")StorageError
Raised on WAL, snapshot, or disk I/O failures — disk-full conditions, permission errors on storage files, or a corrupt WAL sequence. A write attempted against a read-only or sealed collection also surfaces as a StorageError.
try:
docs.add(vector=[1.0, 0.0])
except elips.StorageError as e:
print(f"Storage I/O failure: {e}")Retry Strategies & Best Practices
- In multi-process worker environments (Gunicorn, Celery), wrap database opening in a
LockConflictretry loop with exponential back-off, or open secondary processes withaccess_mode="read_only". - Catch
DimensionMismatchandInvalidVectorat your ingest boundary to reject bad input before it reaches a batch write. - Prefer
get_or_create_collection()overget_collection()when a missing collection should be created lazily rather than raiseCollectionNotFoundError. - Catch the base
ElipsErroras a last-resort handler so new error types are never silently swallowed as generic exceptions.