deepset-ai/haystack · error · DeserializationError

Refusing to deserialize '{handle}': it resolves to a builtin

Error message

Refusing to deserialize '{handle}': it resolves to a builtin that is not a type and cannot be used as a type annotation or class reference. If you trust the source of this data, load it with unsafe=True to bypass deserialization safety checks.

What it means

A serialized type annotation handle resolved to a builtin that is not a class (type), e.g. 'len' or 'print'. Haystack only allows builtins that are actual types to be used as type annotations or class references during deserialization. This prevents invoking arbitrary non-type builtins from untrusted data.

Source

Thrown at haystack/core/serialization_security.py:543

def _check_builtin_is_type(resolved: object, handle: str) -> None:
    """
    Reject a `builtins` member resolved in a type/class context that is not a `type`.

    Used by `deserialize_type` and `import_class_by_name`, which resolve type annotations and class
    references — always classes. Requiring the resolved `builtins` member to be a `type` lets every
    builtin type through (e.g. `str`, `memoryview`) while rejecting every builtin *function* (e.g.
    `eval`, `exec`, `getattr`), with no denylist to maintain. Bypassed in `unsafe=True` mode.

    :param resolved:
        The object resolved from the serialized handle.
    :param handle:
        The original serialized handle, used only for the error message.
    """
    if _is_unsafe_deserialization():
        return
    if not isinstance(resolved, type):
        raise DeserializationError(
            f"Refusing to deserialize '{handle}': it resolves to a builtin that is not a type and "
            f"cannot be used as a type annotation or class reference. If you trust the source of "
            f"this data, load it with unsafe=True to bypass deserialization safety checks."
        )


@contextmanager
def _deserialization_context(allowed_modules: Iterable[str] | None = None, unsafe: bool = False) -> Iterator[None]:
    """
    Context manager that activates a per-call deserialization context.

    Patterns from `allowed_modules` are appended to the parent context's patterns, and `unsafe`
    is OR-ed with the parent's `unsafe` flag — so this never narrows the active permissions.
    The previous context is restored on exit.
    """
    parent = _get_context()
    extra = parent.extra_allowed + (tuple(allowed_modules) if allowed_modules else ())
    merged_unsafe = parent.unsafe or unsafe

View on GitHub (pinned to e318778c9b)

Solutions

  1. Correct the handle in the serialized data to a real type, e.g. 'builtins.dict' or a full class path
  2. If the builtin is intentionally needed as data (not a type), restructure the payload so it is not deserialized as a type annotation
  3. Load with unsafe=True if you trust the source

Example fix

// before (YAML)
data_type: "builtins.len"
// after
data_type: "builtins.str"
Defensive patterns

Strategy: validation

Validate before calling

import builtins
import importlib
def is_valid_type_handle(handle: str) -> bool:
    try:
        module, _, name = handle.rpartition(".")
        obj = getattr(builtins if module == "builtins" else importlib.import_module(module), name)
        return isinstance(obj, type)
    except Exception:
        return False

Type guard

import builtins
def resolves_to_type(handle: str) -> bool:
    name = handle.split(".")[-1]
    return isinstance(getattr(builtins, name, None), type)

Try / catch

from haystack.core.errors import DeserializationError
try:
    pipe = Pipeline.loads(yaml_str)
except DeserializationError as e:
    if "not a type" in str(e):
        print("Fix the type annotation handle in the serialized data:", e)
    else:
        raise

Prevention

When it happens

Trigger: deserialize_type or _import_class_by_name resolving a handle like 'builtins.len' or 'print' where isinstance(resolved, type) is False, while unsafe mode is off.

Common situations: Typos in serialized type fields (e.g. 'dict' misspelled as 'dict_items' or 'open'); hand-written pipeline YAML annotations; data produced by buggy exporters.

Related errors


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