{"record":{"id":"253213ab6e8aa2fb","repo":"unclecode/crawl4ai","slug":"hook-hook-name-must-be-a-callable-function-go","errorCode":null,"errorMessage":"Hook '{hook_name}' must be a callable function, got {type(hook_func)}","messagePattern":"Hook '(.+?)' must be a callable function, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crawl4ai/utils.py","lineNumber":3726,"sourceCode":"        Dictionary mapping hook point names to string representations of the functions.\n\n    Example:\n        >>> async def my_hook(page, context, **kwargs):\n        ...     await page.set_viewport_size({\"width\": 1920, \"height\": 1080})\n        ...     return page\n        >>>\n        >>> hooks_dict = {\"on_page_context_created\": my_hook}\n        >>> api_hooks = hooks_to_string(hooks_dict)\n        >>> # api_hooks is now ready to use with Docker API\n\n    Raises:\n        ValueError: If a hook is not callable or source cannot be extracted\n    \"\"\"\n    result = {}\n\n    for hook_name, hook_func in hooks.items():\n        if not callable(hook_func):\n            raise ValueError(f\"Hook '{hook_name}' must be a callable function, got {type(hook_func)}\")\n\n        try:\n            # Get the source code of the function\n            source = inspect.getsource(hook_func)\n            # Remove any leading indentation to get clean source\n            source = textwrap.dedent(source)\n            result[hook_name] = source\n        except (OSError, TypeError) as e:\n            raise ValueError(\n                f\"Cannot extract source code for hook '{hook_name}'. \"\n                f\"Make sure the function is defined in a file (not interactively). Error: {e}\"\n            )\n\n    return result\n","sourceCodeStart":3708,"sourceCodeEnd":3741,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/utils.py#L3708-L3741","documentation":"hooks_to_string (crawl4ai/utils.py:3726) raises ValueError when a value in the hooks dict passed to it is not callable. The function serializes hook functions into source-code strings for the Docker/remote API; before extracting source with inspect.getsource it asserts each hook is a function. Passing a string of code, a module, None, or an already-serialized source string triggers this.","triggerScenarios":"Calling hooks_to_string({\"on_page_context_created\": \"def hook(ctx): ...\"}) — passing source text instead of a function object; passing a class or builtin; a dict built from JSON config where values are strings by construction.","commonSituations":"Porting a JSON/YAML hook config to the Python API and forgetting to define actual functions; passing hook names instead of hook callables; refactoring that replaces functions with functools.partial of a non-callable.","solutions":["Pass actual function objects, not source strings: define def my_hook(ctx): ... and use hooks_to_string({\"on_page_context_created\": my_hook}).","If config arrives as JSON strings, exec/compile them into functions first (only for trusted input) or use the server's declarative hook specs instead.","Add a unit assertion callable(h) for every hook before calling hooks_to_string to fail fast with your own message."],"exampleFix":"# before\napi_hooks = hooks_to_string({\"on_page_context_created\": \"my_hook_source\"})\n\n# after\ndef my_hook(page, context, **kwargs):\n    return None\napi_hooks = hooks_to_string({\"on_page_context_created\": my_hook})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"from typing import Callable\n\ndef is_valid_hook_dict(hooks: dict) -> bool:\n    return bool(hooks) and all(\n        isinstance(k, str) and callable(v) and not isinstance(v, (str, bytes))\n        for k, v in hooks.items()\n    )","tryCatchPattern":"try:\n    api_hooks = hooks_to_string(hooks)\nexcept ValueError as e:\n    if \"must be a callable\" in str(e):\n        hooks = {k: v for k, v in hooks.items() if callable(v)}  # drop bad entries\n    raise","preventionTips":["Always define hooks as named module-level functions","Assert callable() on config-loaded hook values before serialization"],"tags":["hooks","validation","crawl4ai","docker-api"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}