{"record":{"id":"fb6505f368d59b61","repo":"unclecode/crawl4ai","slug":"cannot-extract-source-code-for-hook-hook-name","errorCode":null,"errorMessage":"Cannot extract source code for hook '{hook_name}'. Make sure the function is defined in a file (not interactively). Error: {e}","messagePattern":"Cannot extract source code for hook '(.+?)'\\. Make sure the function is defined in a file \\(not interactively\\)\\. Error: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crawl4ai/utils.py","lineNumber":3735,"sourceCode":"        >>> # 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":3717,"sourceCodeEnd":3741,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/utils.py#L3717-L3741","documentation":"hooks_to_string (crawl4ai/utils.py:3735) raises ValueError when inspect.getsource(hook_func) raises OSError/TypeError. getsource can only retrieve source for functions defined in a real file that the interpreter can locate — lambdas passed directly, functions defined in the REPL/Jupyter (partially), C builtins, or objects whose source file is gone/moved all fail here. The function needs the literal source text because it ships hooks to a Docker-based crawl4ai API.","triggerScenarios":"Using a lambda as a hook: hooks_to_string({\"on_url_context\": lambda ctx: None}); defining the hook interactively in ipython; decorating with a wrapper whose module file was deleted after import; passing functools.partial objects or callables implemented in C.","commonSituations":"Quick notebook experiments with lambda hooks; hooks defined inside exec'd dynamic code; source tree modified after the process imported the module (linecache miss).","solutions":["Replace lambdas with module-level def functions so inspect.getsource can find them.","Ensure the hook lives in an importable .py file on disk that stays in place during the call.","Avoid functools.partial/builtins as hooks — write a plain def that forwards to them.","If you must generate hooks dynamically, write them to a temp .py file, import it, then pass that function."],"exampleFix":"# before\nhooks_to_string({\"on_page_context_created\": lambda ctx: None})\n\n# after\n# hooks.py\ndef on_page_context_created(page, context, **kwargs):\n    return None\n\nfrom hooks import on_page_context_created\nhooks_to_string({\"on_page_context_created\": on_page_context_created})","handlingStrategy":"type-guard","validationCode":"import inspect\n\ndef hook_source_extractable(func) -> bool:\n    if not callable(func):\n        return False\n    try:\n        inspect.getsource(func)\n        return True\n    except (OSError, TypeError):\n        return False","typeGuard":"import inspect, types\n\ndef is_serializable_hook(func) -> bool:\n    return (\n        callable(func)\n        and not isinstance(func, types.BuiltinFunctionType)\n        and getattr(func, \"__name__\", \"<lambda>\") != \"<lambda>\"\n        and hook_source_extractable(func)\n    )","tryCatchPattern":"try:\n    api_hooks = hooks_to_string(hooks)\nexcept ValueError as e:\n    if \"source code\" in str(e):\n        raise ValueError(f\"Move hook to a module-level def in a .py file: {e}\")\n    raise","preventionTips":["Never use lambdas or partials as serializable hooks","Keep hooks in stable .py files present for the process lifetime","For dynamic hooks, generate a temp module and import it before serialization"],"tags":["hooks","inspect","source-code","crawl4ai"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}