{"record":{"id":"861480a5648ceb7a","repo":"deepset-ai/haystack","slug":"a-functionhook-requires-at-least-one-of-function","errorCode":null,"errorMessage":"A FunctionHook requires at least one of `function` or `async_function` to be set.","messagePattern":"A FunctionHook requires at least one of `function` or `async_function` to be set\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"haystack/hooks/from_function.py","lineNumber":50,"sourceCode":"    \"\"\"\n\n    def __init__(\n        self,\n        function: Callable[[State], None] | None = None,\n        async_function: Callable[[State], Awaitable[None]] | None = None,\n    ) -> None:\n        \"\"\"\n        Initialize the hook with a synchronous function, an async function, or both.\n\n        :param function: The synchronous function invoked by `run`. Must be a regular function — coroutine functions\n            should be passed to `async_function` instead. Either `function` or `async_function` (or both) must be set.\n        :param async_function: Optional coroutine function awaited by `run_async`. When only `async_function` is set,\n            `run` raises a `RuntimeError`. When only `function` is set, `run_async` calls `function`.\n        :raises ValueError: If neither is set, if `function` is a coroutine function, if `async_function` is not, or\n            if a provided function does not declare a `State`-typed parameter.\n        \"\"\"\n        if function is None and async_function is None:\n            raise ValueError(\"A FunctionHook requires at least one of `function` or `async_function` to be set.\")\n        if function is not None and inspect.iscoroutinefunction(function):\n            raise ValueError(\n                f\"`function` must be a synchronous function. '{function.__name__}' is a coroutine function. \"\n                \"Pass it as `async_function` instead.\"\n            )\n        if async_function is not None and not inspect.iscoroutinefunction(async_function):\n            raise ValueError(\n                f\"`async_function` must be a coroutine function defined with `async def`. \"\n                f\"Got '{getattr(async_function, '__name__', repr(async_function))}'.\"\n            )\n        for func in (function, async_function):\n            if func is not None and not _takes_single_state_argument(func):\n                raise ValueError(\n                    f\"Hook function '{func.__name__}' must take a single parameter annotated with `State` \"\n                    \"(e.g. `def my_hook(state: State) -> None`).\"\n                )\n        self.function = function\n        self.async_function = async_function","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/hooks/from_function.py#L32-L68","documentation":"FunctionHook wraps a sync and/or async callable and raises ValueError in __init__ (haystack/hooks/from_function.py:50) when neither function nor async_function is provided. A hook with no callable would have no behavior to run, so construction fails immediately.","triggerScenarios":"FunctionHook() with both arguments None/omitted; passing a variable that is None at call time; conditionally assigning the callable and having the condition fail.","commonSituations":"Refactoring where the callable was deleted or renamed; loading a hook spec from config where the function reference didn't resolve; typos so the kwarg never receives the function.","solutions":["Pass at least one callable: FunctionHook(function=my_sync_fn) or FunctionHook(async_function=my_async_fn).","Check why the variable holding the function is None (import failure, wrong name).","Guard at the call site: assert callable(fn) before constructing."],"exampleFix":"// before\nhook = FunctionHook()  # both None\n// after\nhook = FunctionHook(function=my_sync_fn, async_function=my_async_fn)","handlingStrategy":"validation","validationCode":"def make_function_hook(fn=None, async_fn=None):\n    if fn is None and async_fn is None:\n        raise ValueError(\"provide function and/or async_function\")\n    return FunctionHook(function=fn, async_function=async_fn)","typeGuard":"def is_callable_or_none(f) -> bool:\n    return f is None or callable(f)","tryCatchPattern":"try:\n    hook = FunctionHook(function=fn, async_function=afn)\nexcept ValueError as e:\n    if \"at least one of\" in str(e):\n        logger.error(\"FunctionHook built with no callable; check wiring of %r/%r\", fn, afn)\n        raise\n    raise","preventionTips":["Never construct FunctionHook with variables that may be None","Import and smoke-test the wrapped function before wiring the hook","Assert callable(fn) at pipeline assembly time","Prefer explicit keyword arguments to avoid dropped positional args"],"tags":["validation","constructor","hooks","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}