elips/docs
Reference · Python

Exceptions & Error Handling

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

python
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 failure

ConfigError

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.

python
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.

python
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.

python
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.

python
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 (alias NotFoundError) — raised by collection.update() (and other id-targeted operations) when the record id does not exist.
  • CollectionNotFoundError — raised by client.get_collection(name) when the named collection does not exist. Use get_or_create_collection() if you want lazy creation instead.
python
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.

python
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.

python
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.

python
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.

python
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 LockConflict retry loop with exponential back-off, or open secondary processes with access_mode="read_only".
  • Catch DimensionMismatch and InvalidVector at your ingest boundary to reject bad input before it reaches a batch write.
  • Prefer get_or_create_collection() over get_collection() when a missing collection should be created lazily rather than raise CollectionNotFoundError.
  • Catch the base ElipsError as a last-resort handler so new error types are never silently swallowed as generic exceptions.