{"record":{"id":"acd11e533bd2ba65","repo":"immich-app/immich","slug":"libann-is-not-available","errorCode":null,"errorMessage":"libann is not available!","messagePattern":"libann is not available!","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"machine-learning/immich_ml/sessions/ann/loader.py","lineNumber":54,"sourceCode":"\n\nclass _Singleton(type, Newable[T]):\n    _instances: dict[_Singleton[T], Newable[T]] = {}\n\n    def __call__(cls, *args: Any, **kwargs: Any) -> Newable[T]:\n        if cls not in cls._instances:\n            obj: Newable[T] = super(_Singleton, cls).__call__(*args, **kwargs)\n            cls._instances[cls] = obj\n        else:\n            obj = cls._instances[cls]\n            obj.new()\n        return obj\n\n\nclass Ann(metaclass=_Singleton):\n    def __init__(self, log_level: int = 3, tuning_level: int = 1, tuning_file: str | None = None) -> None:\n        if not is_available:\n            raise RuntimeError(\"libann is not available!\")\n        if tuning_level == 0 and tuning_file is None:\n            raise ValueError(\"tuning_level == 0 reads existing tuning information and requires a tuning_file\")\n        if tuning_level < 0 or tuning_level > 3:\n            raise ValueError(\"tuning_level must be 0 (load from tuning_file), 1, 2 or 3.\")\n        if log_level < 0 or log_level > 5:\n            raise ValueError(\"log_level must be 0 (trace), 1 (debug), 2 (info), 3 (warning), 4 (error) or 5 (fatal)\")\n        self.log_level = log_level\n        self.tuning_level = tuning_level\n        self.tuning_file = tuning_file\n        self.output_shapes: dict[int, tuple[tuple[int], ...]] = {}\n        self.input_shapes: dict[int, tuple[tuple[int], ...]] = {}\n        self.ann: int | None = None\n        self.new()\n\n        if self.tuning_file is not None:\n            # make sure tuning file exists (without clearing contents)\n            # once filled, the tuning file reduces the cost/time of the first\n            # inference after model load by 10s of seconds","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/machine-learning/immich_ml/sessions/ann/loader.py#L36-L72","documentation":"Raised by Ann.__init__ when the module-level is_available flag is False. That flag is set False at import time if CDLL('libmali.so') or CDLL('libann.so') raised OSError — i.e. the ARM Mali userspace driver (libmali.so) and/or the Arm NN wrapper library (libann.so) are not present/loadable in the container or host. Ann is a singleton, so this fires on the first Ann() construction (which AnnSession does for .armnn models).","triggerScenarios":"Constructing an AnnSession / loading a model with model_format=ARMNN on a host that is not an ARM Mali GPU device, or in a container image built without libann.so and libmali.so mounted. Also triggered if the libraries exist but have unresolved dependencies (wrong glibc, missing libstdc++), which CDLL surfaces as OSError.","commonSituations":"Running the default (non-ARM) immich-ml image on x86_64; forgetting to mount the vendor libmali.so blob into the container; LD_LIBRARY_PATH not including the directory holding libann.so; ABI mismatch between libmali.so and the host kernel mali driver; using the ARMNN image on a Rockchip board that should use RKNN instead.","solutions":["Confirm you actually intend to use ARMNN: switch model_format to ONNX (or RKNN on Rockchip) unless you are on a Mali GPU platform.","Use the immich-ml ARMNN-tagged image and ensure libmali.so is mounted from the host into /usr/lib if required by that image.","Run `ldd /usr/lib/libann.so` and `ldd /usr/lib/libmali.so` in the container and install/fix every 'not found' dependency.","Set LD_LIBRARY_PATH (or configure the runtime) to the directory containing both libraries and restart the service.","Check the container's debug log for the original OSError line ('Could not load ANN shared libraries, using ONNX') which names the missing library."],"exampleFix":"# before\n# x86 host, ARMNN image without libs -> is_available=False\nsession = AnnSession(model_path)   # RuntimeError: libann is not available!\n\n# after\n# docker-compose.yml\nservices:\n  immich-machine-learning:\n    image: ghcr.io/immich-app/immich-machine-learning:cuda\n    # use the right variant; or mount mali on ARM:\n    #   volumes:\n    #     - /usr/lib/libmali.so:/usr/lib/libmali.so:ro\nmodel = InferenceModel('immich-app/X', model_format=ModelFormat.ONNX)","handlingStrategy":"type-guard","validationCode":"from immich_ml.sessions.ann.loader import is_available\n\ndef ensure_armnn_available() -> None:\n    if not is_available:\n        raise RuntimeError(\n            \"libann/libmali not loadable; use the ARMNN image variant with libmali.so mounted, \"\n            \"or switch model_format to ONNX. See the 'Could not load ANN shared libraries' debug line.\"\n        )\n\n# call before constructing an AnnSession:\nensure_armnn_available()","typeGuard":"from immich_ml.sessions.ann.loader import is_available\n\ndef armnn_runtime_available() -> bool:\n    return bool(is_available)","tryCatchPattern":"from immich_ml.sessions.ann.loader import is_available\nfrom immich_ml.sessions.ort import OrtSession\n\ntry:\n    session = AnnSession(model_path)\nexcept RuntimeError as e:\n    if not is_available:\n        log.warning(\"ARMNN unavailable, falling back to ONNX session\")\n        session = OrtSession(model_path.with_suffix('.onnx'))\n    else:\n        raise","preventionTips":["Gate model_format selection on armnn.is_available / rknn.is_available at startup rather than at first load.","In container builds, run `ldd` on libann.so and libmali.so as a build-time assertion.","Document the exact volume mounts required for libmali.so in the deployment guide."],"tags":["armnn","native-library","gpu","container","mali"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}