{"record":{"id":"aba47da77bafd563","repo":"deepset-ai/haystack","slug":"serialization-of-nested-functions-is-not-supported","errorCode":null,"errorMessage":"Serialization of nested functions is not supported.","messagePattern":"Serialization of nested functions is not supported\\.","errorType":"exception","errorClass":"SerializationError","httpStatus":null,"severity":"error","filePath":"haystack/utils/callable_serialization.py","lineNumber":47,"sourceCode":"    Serializes a callable to its full path.\n\n    :param callable_handle: The callable to serialize\n    :return: The full path of the callable\n    \"\"\"\n    try:\n        full_arg_spec = inspect.getfullargspec(callable_handle)\n        is_instance_method = bool(full_arg_spec.args and full_arg_spec.args[0] == \"self\")\n    except TypeError:\n        is_instance_method = False\n    if is_instance_method:\n        raise SerializationError(\"Serialization of instance methods is not supported.\")\n\n    # __qualname__ contains the fully qualified path we need for classmethods and staticmethods\n    qualname = getattr(callable_handle, \"__qualname__\", \"\")\n    if \"<lambda>\" in qualname:\n        raise SerializationError(\"Serialization of lambdas is not supported.\")\n    if \"<locals>\" in qualname:\n        raise SerializationError(\"Serialization of nested functions is not supported.\")\n\n    name = qualname or callable_handle.__name__\n\n    # Get the full package path of the function\n    module = inspect.getmodule(callable_handle)\n    if module is not None:\n        full_path = f\"{module.__name__}.{name}\"\n    else:\n        full_path = name\n\n    # Serialization succeeds, but a denied builtin (e.g. `eval`) won't reload without `unsafe=True`.\n    if _is_denied_builtin(callable_handle):\n        logger.warning(\n            \"Serialized callable '{full_path}' is a builtin that is blocked during deserialization; \"\n            \"the resulting pipeline will only be loadable with unsafe=True.\",\n            full_path=full_path,\n        )\n","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/callable_serialization.py#L29-L65","documentation":"serialize_callable raises SerializationError when __qualname__ contains '<locals>', meaning the callable is a nested function defined inside another function. Such a function cannot be imported by path and therefore cannot be deserialized, so serialization is refused.","triggerScenarios":"Defining a callback inside a function (e.g. def main(): def splitter(t): ...; TextSplitter(splitting_function=splitter)) then calling to_dict on the component or pipeline.","commonSituations":"Callbacks defined in notebooks, test fixtures, or CLI entry points; factory functions that build pipelines with locally-defined callbacks.","solutions":["Move the function to module level so it has an importable path","Return a module-level function from the factory instead of defining it inline","Promote the logic to a small callable class defined at module level"],"exampleFix":"// before\ndef build():\n    def splitter(t): return t.split()\n    return TextSplitter(splitting_function=splitter)\n// after\n# module level\ndef splitter(t): return t.split()\ndef build():\n    return TextSplitter(splitting_function=splitter)","handlingStrategy":"validation","validationCode":"def is_nested(fn) -> bool:\n    return \"<locals>\" in getattr(fn, \"__qualname__\", \"\")","typeGuard":"import types\n\ndef is_module_level(fn) -> bool:\n    return isinstance(fn, types.FunctionType) and \"<locals>\" not in fn.__qualname__","tryCatchPattern":"try:\n    serialized = pipeline.dumps()\nexcept SerializationError as e:\n    logger.error(\"nested function passed as callable: %s\", e)\n    raise","preventionTips":["Define callbacks at module top level, never inside functions","Move notebook-defined callbacks into a helper module","Keep factory functions returning module-level function references"],"tags":["python","serialization","nested-function","callable","haystack"],"backgroundTag":"unserializable-callable","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}