deepset-ai/haystack · error · DeserializationError

Could not import '{callable_handle}' as a module or callable

Error message

Could not import '{callable_handle}' as a module or callable.

What it means

deserialize_callable exhausted every resolution strategy against the module allowlist and still could not import the handle, so after emitting the standard allowlist error it raises DeserializationError. Neither the top-level module nor any dotted path could be resolved to a callable.

Source

Thrown at haystack/utils/callable_serialization.py:171

        # The module check also does not stop import primitives that live inside an allowlisted
        # namespace (e.g. `haystack...thread_safe_import`), which are gateways to code execution
        # equivalent to the denied builtin `__import__`. Block them too.
        _check_not_denied_callable(attr_value, callable_handle)

        # Refuse the deserializer's own machinery — the allowlist-administration function
        # (`allow_deserialization_module`) and the resolution helpers (`deserialize_callable`,
        # `deserialize_type`, `import_class_by_name`). They live in the allowlisted `haystack`
        # namespace, so the module checks above admit them, but resolving them from serialized data
        # lets a hostile pipeline register them as Jinja custom filters, disarm the allowlist with
        # `'*'`, and then resolve and invoke arbitrary callables such as `os.system`.
        _check_not_deserialization_internal(attr_value, callable_handle)

        return attr_value

    # Nothing on the allowlist was importable. Surface the standard allowlist error when the
    # top-level module is untrusted; otherwise report a plain resolution failure.
    _check_module_allowed(callable_handle)
    raise DeserializationError(f"Could not import '{callable_handle}' as a module or callable.")

View on GitHub (pinned to e318778c9b)

Solutions

  1. Install the missing package or add your module to sys.path/PYTHONPATH
  2. Ensure the module is trusted/allowlisted for haystack deserialization (configure the allowlist mechanism)
  3. Fix typos in the serialized callable handle
  4. Verify importability with importlib.import_module on the top-level module

Example fix

// before
python -c "import my_custom_components"  # ModuleNotFoundError
// after
pip install -e .  # or export PYTHONPATH=/path/to/project
Defensive patterns

Strategy: try-catch

Validate before calling

import importlib

def module_importable(handle: str) -> bool:
    try:
        importlib.import_module(handle.split(".")[0])
        return True
    except ImportError:
        return False

Try / catch

try:
    pipeline = Pipeline.loads(yaml_str)
except DeserializationError as e:
    if "Could not import" in str(e):
        handle = extract_handle(str(e))
        logger.error("install missing package or allowlist module for %s", handle)
    raise

Prevention

When it happens

Trigger: from_dict/deserialize_callable with a handle whose module is not importable (not installed, wrong PYTHONPATH) or not on the deserialization allowlist, or a malformed dotted path.

Common situations: Loading a pipeline referencing a third-party package not installed in the current environment; custom components module not on sys.path; module blocked by haystack's safe-deserialization allowlist; typos in module names.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/f2053c2c81e462c1. Report an issue: GitHub.