deepset-ai/haystack · error · SerializationError

Serialization of lambdas is not supported.

Error message

Serialization of lambdas is not supported.

What it means

serialize_callable inspects __qualname__ and raises SerializationError when it contains '<lambda>', because a lambda has no importable module path and cannot be reconstructed by deserialize_callable. Only importable, named callables can be serialized.

Source

Thrown at haystack/utils/callable_serialization.py:45

def serialize_callable(callable_handle: Callable) -> str:
    """
    Serializes a callable to its full path.

    :param callable_handle: The callable to serialize
    :return: The full path of the callable
    """
    try:
        full_arg_spec = inspect.getfullargspec(callable_handle)
        is_instance_method = bool(full_arg_spec.args and full_arg_spec.args[0] == "self")
    except TypeError:
        is_instance_method = False
    if is_instance_method:
        raise SerializationError("Serialization of instance methods is not supported.")

    # __qualname__ contains the fully qualified path we need for classmethods and staticmethods
    qualname = getattr(callable_handle, "__qualname__", "")
    if "<lambda>" in qualname:
        raise SerializationError("Serialization of lambdas is not supported.")
    if "<locals>" in qualname:
        raise SerializationError("Serialization of nested functions is not supported.")

    name = qualname or callable_handle.__name__

    # Get the full package path of the function
    module = inspect.getmodule(callable_handle)
    if module is not None:
        full_path = f"{module.__name__}.{name}"
    else:
        full_path = name

    # Serialization succeeds, but a denied builtin (e.g. `eval`) won't reload without `unsafe=True`.
    if _is_denied_builtin(callable_handle):
        logger.warning(
            "Serialized callable '{full_path}' is a builtin that is blocked during deserialization; "
            "the resulting pipeline will only be loadable with unsafe=True.",
            full_path=full_path,

View on GitHub (pinned to e318778c9b)

Solutions

  1. Replace the lambda with a named module-level function
  2. Define the logic in a small helper function in an importable module
  3. If configuration-only customization is needed, use a supported parameter instead of a callable

Example fix

// before
TextSplitter(splitting_function=lambda t: t.lower())
// after
# mymodule/helpers.py
def lowercase(t): return t.lower()
TextSplitter(splitting_function=lowercase)
Defensive patterns

Strategy: validation

Validate before calling

def is_lambda(fn) -> bool:
    return getattr(fn, "__name__", "") == "<lambda>"

# reject before passing to component

Type guard

import types
from collections.abc import Callable

def is_named_module_level_function(fn: Callable) -> bool:
    return isinstance(fn, types.FunctionType) and "<" not in fn.__qualname__

Try / catch

try:
    serialized = component.to_dict()
except SerializationError as e:
    if "lambda" in str(e):
        logger.error("replace lambda with a named module-level function")
    raise

Prevention

When it happens

Trigger: Passing a lambda as a callable parameter (e.g. TextSplitter(splitting_function=lambda t: t.lower())) and then calling to_dict or pipeline.dumps().

Common situations: Quick inline lambdas used in scripts or notebooks that later get saved to YAML; pipeline configs built at runtime.

Related errors


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