immich-app/immich · error · ValueError
Cannot load model!
Error message
Cannot load model!
What it means
Raised by Ann.load() when the native libann.load() call returns a negative network id. At this point the file exists and has a valid extension, but the Arm NN runtime could not parse/load it. The native return code is negative on any internal failure (corrupt file, unsupported operator, I/O error reading the network, cache deserialization failure).
Source
Thrown at machine-learning/immich_ml/sessions/ann/loader.py:124
if not exists(model_path):
raise ValueError("model_path must point to an existing file!")
save_cached_network = False
if cached_network_path is not None and not exists(cached_network_path):
save_cached_network = True
# create empty model cache file
open(cached_network_path, "a").close()
net_id: int = libann.load(
self.ann,
model_path.encode(),
fast_math,
fp16,
save_cached_network,
cached_network_path.encode() if cached_network_path is not None else None,
)
if net_id < 0:
raise ValueError("Cannot load model!")
self.input_shapes[net_id] = tuple(
self.shape(net_id, input=True, index=i) for i in range(self.tensors(net_id, input=True))
)
self.output_shapes[net_id] = tuple(
self.shape(net_id, input=False, index=i) for i in range(self.tensors(net_id, input=False))
)
return net_id
def unload(self, network_id: int) -> None:
libann.unload(self.ann, network_id)
del self.output_shapes[network_id]
def execute(self, network_id: int, input_tensors: list[NDArray[np.float32]]) -> list[NDArray[np.float32]]:
if not isinstance(input_tensors, list):
raise ValueError("input_tensors needs to be a list!")
net_input_shapes = self.input_shapes[network_id]
if len(input_tensors) != len(net_input_shapes):View on GitHub (pinned to 199723261c)
Solutions
- Check the file size/hash against the source repo to detect truncation or corruption; re-download if mismatched.
- Delete any cached_network_path you passed in (or let save_cached_network recreate it) so a stale cache is not deserialized.
- Inspect Arm NN / libann logs (raise Ann log_level to 1 or 0) for the specific parser error, then re-export the model avoiding the unsupported op.
- Re-export the ONNX model to an opset/version Arm NN supports and retry.
- Ensure no other process is writing the model file while load() runs.
Example fix
# before
ann = Ann(log_level=3, tuning_level=1)
net_id = ann.load('/models/model.armnn', cached_network_path='/cache/model.ann')
# ValueError: Cannot load model! (stale /cache/model.ann from old libann)
# after
from pathlib import Path
Path('/cache/model.ann').unlink(missing_ok=True)
ann = Ann(log_level=0) # trace to see the parser error
net_id = ann.load('/models/model.armnn') Defensive patterns
Strategy: try-catch
Validate before calling
import hashlib
from pathlib import Path
def validate_model_integrity(model_path: str, expected_sha256: str | None = None) -> None:
p = Path(model_path)
if not p.is_file() or p.stat().st_size == 0:
raise ValueError(f"{model_path} missing or empty; cannot load")
if expected_sha256:
h = hashlib.sha256()
h.update(p.read_bytes())
if h.hexdigest() != expected_sha256:
raise ValueError(f"{model_path} hash mismatch; re-download")
# call before Ann.load():
validate_model_integrity(model_path, expected_sha256=EXPECTED_HASH) Type guard
from pathlib import Path
def looks_like_complete_model(model_path: str, min_size: int = 1024) -> bool:
p = Path(model_path)
return p.is_file() and p.stat().st_size > min_size Try / catch
try:
net_id = ann.load(model_path, cached_network_path=cache)
except ValueError as e:
if 'Cannot load model' in str(e):
log.error("libann rejected %s; removing stale cache and re-downloading", model_path)
Path(model_path).unlink(missing_ok=True)
if cache:
Path(cache).unlink(missing_ok=True)
redownload(model_path)
net_id = ann.load(model_path) # single retry without stale cache
else:
raise Prevention
- Treat cached_network_path as disposable: delete it whenever libann or the model version changes.
- Verify file hashes/sizes after download to catch truncation before load().
- Re-export models using ops supported by the bundled Arm NN version, and pin that version in CI.
When it happens
Trigger: Passing a truncated or partially-written .armnn/.onnx/.tflite file to libann; a model using ops not supported by the Arm NN version bundled in libann.so; a corrupt cached_network_path that libann tries to deserialize; mismatch between the model format and libann's parser; disk read error during load.
Common situations: Interrupted download leaving a half-size model file (passes exists() check but fails to parse); using an ONNX opset newer than Arm NN supports; reusing a cached_network_path produced by a different libann version; bit-rot on the volume holding the model; concurrent writers corrupting the file.
Related errors
- libann is not available!
- Failed to load model '{model.model_name}'
- Model file not found: {model_path}
- Unsupported model file type: {model_path.suffix}
- model_path must be a file with extension .armnn, .tflite or
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/131a0f72a48d0419.
Report an issue: GitHub.