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 unsafeView on GitHub (pinned to e318778c9b)
Solutions
- Correct the handle in the serialized data to a real type, e.g. 'builtins.dict' or a full class path
- If the builtin is intentionally needed as data (not a type), restructure the payload so it is not deserialized as a type annotation
- 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
- Only reference real classes (types) in type annotation fields
- Autocomplete/verify builtin names like dict, list, str when editing YAML by hand
- Regenerate serialized pipelines from code instead of editing type fields manually
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
- Refusing to deserialize '{handle}': it resolves to an import
- tool_concurrency_limit must be greater than or equal to 1.
- Document with ID '{doc.id}' has a file path '{file_path}' th
- Refusing to deserialize an OutputAdapter with unsafe=True wh
- Refusing to deserialize an OutputAdapter with custom filters
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/62ac8e7d916f2e90.
Report an issue: GitHub.