{"record":{"id":"8290102199c170f2","repo":"deepset-ai/haystack","slug":"refusing-to-deserialize-an-outputadapter-with-cust","errorCode":null,"errorMessage":"Refusing to deserialize an OutputAdapter with custom filters while loading in safe mode. Custom filters are arbitrary callables that can execute during pipeline loading. If you trust the source of this data, load it with Pipeline.load(..., unsafe=True).","messagePattern":"Refusing to deserialize an OutputAdapter with custom filters while loading in safe mode\\. Custom filters are arbitrary callables that can execute during pipeline loading\\. If you trust the source of this data, load it with Pipeline\\.load\\(\\.\\.\\., unsafe=True\\)\\.","errorType":"exception","errorClass":"DeserializationError","httpStatus":null,"severity":"critical","filePath":"haystack/components/converters/output_adapter.py","lineNumber":187,"sourceCode":"        :param data:\n            The dictionary to deserialize from.\n        :returns:\n            The deserialized component.\n        \"\"\"\n        init_params = data.get(\"init_parameters\", {})\n\n        # `unsafe=True` swaps the Jinja sandbox for a NativeEnvironment that executes arbitrary code.\n        # Honor it from serialized data only when the whole pipeline is being loaded in unsafe mode;\n        # otherwise a hostile pipeline could disable the sandbox on its own in default safe mode.\n        if init_params.get(\"unsafe\") and not _is_unsafe_deserialization():\n            raise DeserializationError(\n                \"Refusing to deserialize an OutputAdapter with unsafe=True while loading in safe mode. \"\n                \"If you trust the source of this data, load it with Pipeline.load(..., unsafe=True).\"\n            )\n\n        custom_filters = init_params.get(\"custom_filters\", {})\n        if custom_filters and not _is_unsafe_deserialization():\n            raise DeserializationError(\n                \"Refusing to deserialize an OutputAdapter with custom filters while loading in safe mode. \"\n                \"Custom filters are arbitrary callables that can execute during pipeline loading. \"\n                \"If you trust the source of this data, load it with Pipeline.load(..., unsafe=True).\"\n            )\n\n        init_params[\"output_type\"] = deserialize_type(init_params[\"output_type\"])\n\n        if custom_filters:\n            init_params[\"custom_filters\"] = {\n                name: deserialize_callable(filter_func) if filter_func else None\n                for name, filter_func in custom_filters.items()\n            }\n        return default_from_dict(cls, data)\n","sourceCodeStart":169,"sourceCodeEnd":201,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/converters/output_adapter.py#L169-L201","documentation":"OutputAdapter.from_dict rejects serialized pipelines that carry custom_filters, because custom filters are arbitrary Python callables embedded in serialized data and would execute during pipeline loading — a code-execution vector when loading untrusted files. Safe mode refuses; only unsafe loading accepts them.","triggerScenarios":"Pipeline.load/loads in default safe mode on a pipeline file whose OutputAdapter init_parameters include a non-empty custom_filters dict.","commonSituations":"Sharing pipelines between teams where one side registered custom Jinja filters; stored pipeline artifacts that reference local filter functions; CI loading contributed pipelines.","solutions":["Load with Pipeline.loads(data, unsafe=True) only if you trust the pipeline source","Recreate the OutputAdapter in code with the filters registered programmatically instead of loading them from the file","Strip custom_filters from the YAML and re-add filters at runtime after loading"],"exampleFix":"// before\npipeline = Pipeline.loads(data_with_filters)\n// after\npipeline = Pipeline.loads(sanitized_data)\nadapter = pipeline.get_component(\"my_adapter\")\nadapter.custom_filters[\"my_filter\"] = my_filter  # re-register locally","handlingStrategy":"try-catch","validationCode":"import yaml\ndef has_custom_filters(path):\n    data = yaml.safe_load(open(path))\n    return any(\n        c.get(\"type\") == \"OutputAdapter\" and c.get(\"init_parameters\", {}).get(\"custom_filters\")\n        for c in data.get(\"components\", []).values()\n    )","typeGuard":null,"tryCatchPattern":"try:\n    pipeline = Pipeline.load(path)\nexcept DeserializationError as e:\n    if \"custom filters\" in str(e):\n        pipeline = rebuild_adapter_in_code(path)  # register filters locally\n    else:\n        raise","preventionTips":["Serialize pipelines without embedding callables; register filters in code","Only use unsafe=True loading for pipelines you authored","Share pipeline templates as code, not as serialized artifacts with filters"],"tags":["python","haystack","deserialization","security","arbitrary-code-execution"],"backgroundTag":"unsafe-deserialization-blocked","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}