deepset-ai/haystack · error · DeserializationError

Could not deserialize type: {type_str}

Error message

Could not deserialize type: {type_str}

What it means

deserialize_type() falls back to looking up the name in builtins then in typing; if the bare (no-dot) name exists in neither, it raises DeserializationError('Could not deserialize type: {type_str}'). This is the terminal failure for unresolvable non-generic type names.

Source

Thrown at haystack/utils/type_serialization.py:285

            return _import_class_by_name(type_str)
        except ImportError as e:
            raise DeserializationError(str(e)) from e

    # No module prefix, check builtins and typing.
    # (None / NoneType / Ellipsis are handled at the top of this function, before they can reach the
    # builtin type gate below which would refuse them for not being types.)
    if hasattr(builtins, type_str):
        resolved = getattr(builtins, type_str)
        # This bare-name path never consults the allowlist. A type annotation must resolve to an
        # actual type, so builtin functions like `eval`/`exec` are rejected while types pass.
        _check_builtin_is_type(resolved, type_str)
        return resolved

    # Then check typing
    if hasattr(typing, type_str):
        return getattr(typing, type_str)

    raise DeserializationError(f"Could not deserialize type: {type_str}")


def thread_safe_import(module_name: str) -> ModuleType:
    """
    Import a module in a thread-safe manner.

    Importing modules in a multi-threaded environment can lead to race conditions.
    This function ensures that the module is imported in a thread-safe manner without having impact
    on the performance of the import for single-threaded environments.

    :param module_name: the module to import
    """
    with _import_lock:
        return importlib.import_module(module_name)


@mark_deserialization_internal
def _import_class_by_name(fully_qualified_name: str) -> Any:

View on GitHub (pinned to e318778c9b)

Solutions

  1. Fully qualify the type with its module path, e.g. 'myapp.models.MyType' instead of 'MyType'
  2. Fix typos in the type string
  3. Register/reference the type via an importable module instead of a locally defined class
  4. Check whether the name exists: `hasattr(builtins, name) or hasattr(typing, name)`

Example fix

// before
deserialize_type("MyModel")
// after
deserialize_type("myapp.models.MyModel")
Defensive patterns

Strategy: validation

Validate before calling

import builtins, typing
def resolvable_bare_name(name):
    if "." in name:
        return True
    return hasattr(builtins, name) or hasattr(typing, name)

Prevention

When it happens

Trigger: deserialize_type('SomeCustomType') where the name has no module prefix and is not a builtin or typing name; typos like 'lits[int]' handled at the arg level; names like 'None' handled earlier so they never reach this.

Common situations: Serializing local classes defined in __main__/notebooks then deserializing elsewhere; typo'd type names in pipeline YAML; custom generic aliases that were not fully qualified.

Related errors


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