Exceptions API
veloxquant_mlx.core.exceptions
Exception hierarchy
There is no common VeloxQuant-specific base exception. Each of the six exceptions independently subclasses a Python builtin, grouped here by that builtin:
ValueError
├── QuantizerConfigError
├── CodebookDimensionMismatch
└── OwnerAlreadyActiveError
FileNotFoundError
└── ArtifactNotFoundError
RuntimeError
├── CyclicPipelineError
└── BlockPoolExhaustedError
Catch a specific exception, or catch its builtin category if you want to
handle a broader class of errors — for example, except ValueError also
catches QuantizerConfigError, CodebookDimensionMismatch, and
OwnerAlreadyActiveError, since all three subclass it.
ArtifactNotFoundError
from veloxquant_mlx.core.exceptions import ArtifactNotFoundError
Raised when a required precomputed artifact (rotation matrix, codebook, or JL matrix) is not found in the artifact store.
When raised:
NpyArtifactStore.load_rotation_matrix()/load_codebook()/load_jl_matrix()when the backing.npyfile does not exist on disk- The equivalent lookups on
MemoryArtifactStorewhen the key was never saved
from veloxquant_mlx.core.exceptions import ArtifactNotFoundError
from veloxquant_mlx.artifacts.npy_store import NpyArtifactStore
store = NpyArtifactStore("./artifacts/")
try:
codebook = store.load_codebook(distribution="gaussian", b=4, d=128)
except ArtifactNotFoundError:
print("Codebook not found. Run calibration first:")
print(" python -m veloxquant_mlx precompute --head_dim 128 --bits 4")
CodebookDimensionMismatch
from veloxquant_mlx.core.exceptions import CodebookDimensionMismatch
Raised when a codebook's shape does not match the expected dimension — for example, when constructing a ScalarCodebook from centroids that aren't a 1-D array, or whose count isn't a power of two.
When raised:
ScalarCodebook(centroids)is given acentroidsarray with more than one dimensionScalarCodebook(centroids)is given a number of centroids that is not a power of 2 (so it doesn't correspond to a whole number of bits)
Fix: Re-run calibration to regenerate a codebook with the expected shape, or check the array you're passing in.
CyclicPipelineError
from veloxquant_mlx.core.exceptions import CyclicPipelineError
Raised when a QuantizationGraph (see veloxquant_mlx/dsa/dag.py) contains a cycle and cannot be reduced to a valid topological order.
When raised:
- Calling the graph's topological-sort step on a
QuantizationGraphwhoseset_next()/add_edge()calls introduced a cycle
QuantizerConfigError
from veloxquant_mlx.core.exceptions import QuantizerConfigError
Raised when a quantizer or KV cache is misconfigured — the most common exception in the library. It covers a range of validation failures across KVCacheFactory, KVCacheBuilder, QuantizerFactory, PreconditionerFactory, CodebookFactory, and individual quantizers/caches.
When raised:
- An unknown
methodis passed toKVCacheConfig/KVCacheFactory.create() head_dimis not a power of 2, orbit_width_inlieris invalid (e.g.< 1, or an empty/mixed-type list)jl_dimorn_outlier_channelsis out of range relative tohead_dimsliding_windowis set for a method that doesn't support it- A quantizer- or preconditioner-specific constraint fails (e.g.
KIVIQuantizer'sboutside[1, 8], or aPreconditionerFactorycall missing a required kwarg likePiorS)
from veloxquant_mlx.core.exceptions import QuantizerConfigError
from veloxquant_mlx.cache.base import KVCacheBuilder, KVCacheConfig
try:
config = KVCacheConfig(method="vecinfer", head_dim=100) # not a power of 2
caches = KVCacheBuilder.for_model(model, config)
except QuantizerConfigError as e:
print(e)
# "KVCacheBuilder: head_dim=100 must be a power of 2."
BlockPoolExhaustedError
from veloxquant_mlx.core.exceptions import BlockPoolExhaustedError
Raised when a BlockPoolAllocator has no free blocks left to satisfy an
allocate() call. Allocation is all-or-nothing — no blocks are handed out
on failure.
When raised:
pool.allocate(...)requests more blocks than are currently free for the requested stream ("k"or"v")- A
PooledKVCacheneeds a new block mid-generation and the shared pool is fully checked out by other requests
Fix: Increase PoolConfig.n_blocks, reduce concurrent request count, or
call release() / free_all() on finished requests sooner.
from veloxquant_mlx.core.exceptions import BlockPoolExhaustedError
from veloxquant_mlx.memory import BlockPoolAllocator, PoolConfig
pool = BlockPoolAllocator(PoolConfig(block_size=16, n_blocks=4))
try:
pool.allocate(stream="k", n_tokens=1000, owner=1)
except BlockPoolExhaustedError as e:
print(e)
See Memory (Block Pool) API for the full allocator reference.
OwnerAlreadyActiveError
from veloxquant_mlx.core.exceptions import OwnerAlreadyActiveError
Raised when an owner id is registered on a BlockPoolAllocator while it's
still checked out to another (unreleased) caller — catches two different
requests accidentally reusing the same owner id, which would otherwise let
one silently free the other's blocks via free_all().
When raised:
pool.register_owner(owner)is called twice for the sameownerbefore afree_all(owner)/release_owner(owner)in between
Not raised for: a second pool.allocate(..., owner=owner) call for an
owner that's already active — that's the normal pattern for a single
request growing over multiple calls (e.g. PoolBackedKVCache appending
more blocks), and allocate() can't otherwise distinguish it from a
collision. Call register_owner() yourself up front if you want
collisions between different callers caught immediately.
Fix: Use a different owner id (e.g. a monotonically increasing request
counter that isn't reused until confirmed released), or call
pool.release_owner(owner) / pool.free_all(owner) before reusing the id.
from veloxquant_mlx.core.exceptions import OwnerAlreadyActiveError
from veloxquant_mlx.memory import BlockPoolAllocator, PoolConfig
pool = BlockPoolAllocator(PoolConfig(block_size=16, n_blocks=64))
pool.register_owner(request_id)
try:
pool.register_owner(request_id) # still active -- likely an id collision
except OwnerAlreadyActiveError as e:
print(e)
See Memory (Block Pool) API for the full allocator reference.