{"record":{"id":"d089a2b45c3f135e","repo":"huggingface/smolagents","slug":"no-function-definition-found-in-the-provided-sourc","errorCode":null,"errorMessage":"No function definition found in the provided source of {tool_function.__name__}. Ensure the input is a standard function.","messagePattern":"No function definition found in the provided source of (.+?)\\. Ensure the input is a standard function\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/smolagents/tools.py","lineNumber":1121,"sourceCode":"    # Get the signature parameters of the tool function\n    sig = inspect.signature(tool_function)\n    # - Add \"self\" as first parameter to tool_function signature\n    new_sig = sig.replace(\n        parameters=[inspect.Parameter(\"self\", inspect.Parameter.POSITIONAL_OR_KEYWORD)] + list(sig.parameters.values())\n    )\n    # - Set the signature of the forward method\n    SimpleTool.forward.__signature__ = new_sig\n\n    # Create and attach the source code of the dynamically created tool class and forward method\n    # - Get the source code of tool_function\n    tool_source = textwrap.dedent(inspect.getsource(tool_function))\n    # - Remove the tool decorator and function definition line\n    lines = tool_source.splitlines()\n    tree = ast.parse(tool_source)\n    #   - Find function definition\n    func_node = next((node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)), None)\n    if not func_node:\n        raise ValueError(\n            f\"No function definition found in the provided source of {tool_function.__name__}. \"\n            \"Ensure the input is a standard function.\"\n        )\n    #   - Extract decorator lines\n    decorator_lines = \"\"\n    if func_node.decorator_list:\n        tool_decorators = [d for d in func_node.decorator_list if isinstance(d, ast.Name) and d.id == \"tool\"]\n        if len(tool_decorators) > 1:\n            raise ValueError(\n                f\"Multiple @tool decorators found on function '{func_node.name}'. Only one @tool decorator is allowed.\"\n            )\n        if len(tool_decorators) < len(func_node.decorator_list):\n            warnings.warn(\n                f\"Function '{func_node.name}' has decorators other than @tool. \"\n                \"This may cause issues with serialization in the remote executor. See issue #1626.\"\n            )\n        decorator_start = tool_decorators[0].end_lineno if tool_decorators else 0\n        decorator_end = func_node.decorator_list[-1].end_lineno","sourceCodeStart":1103,"sourceCodeEnd":1139,"githubUrl":"https://github.com/huggingface/smolagents/blob/30bb1161095dbae2271e6bc3cc4c219cc3897a57/src/smolagents/tools.py#L1103-L1139","documentation":"The @tool decorator inspects the function's source via inspect/ast to strip its definition and decorators for serialization. If no ast.FunctionDef node can be found in the retrieved source, it raises ValueError because it cannot process the callable as a standard function.","triggerScenarios":"Applying @tool to objects whose source contains no plain function definition: lambdas, builtins, C extensions, functools.partial results, callables defined in exec'd/REPL code, or decorated functions where inspect.getsource returns unexpected source.","commonSituations":"Wrapping lambdas or imported native functions as tools; defining tools in Jupyter cells or dynamically generated code where source retrieval misbehaves; double-decorating with wrappers that hide the original function.","solutions":["Convert the callable to a standard `def` function at module level and decorate that","Define tools in real .py files (not notebooks/exec/dynamic strings) so inspect.getsource works","Avoid wrapping lambdas, partials, or builtins with @tool; write an explicit wrapper function"],"exampleFix":"# before\nget_weather = tool(lambda city: fetch(city))\n# after\n@tool\ndef get_weather(city: str) -> str:\n    return fetch(city)","handlingStrategy":"type-guard","validationCode":"import inspect, ast\ndef has_function_def_source(func) -> bool:\n    try:\n        tree = ast.parse(inspect.getsource(func))\n    except (TypeError, OSError):\n        return False\n    return any(isinstance(n, ast.FunctionDef) for n in ast.walk(tree))\n\nassert has_function_def_source(candidate), \"define tool as a plain def in a .py file\"","typeGuard":"def is_wrappable_as_tool(func) -> bool:\n    return inspect.isfunction(func) and not inspect.isbuiltin(func) and has_function_def_source(func)","tryCatchPattern":"try:\n    my_tool = tool(candidate)\nexcept ValueError as e:\n    if \"No function definition\" in str(e):\n        def wrapper(arg: str) -> str:\n            return candidate(arg)\n        wrapper.__name__ = getattr(candidate, '__name__', 'tool_fn')\n        my_tool = tool(wrapper)\n    else:\n        raise","preventionTips":["Define tools as plain module-level def functions in real files","Avoid decorating lambdas, partials, builtins, or notebook-defined callables"],"tags":["tool-decorator","source-inspection","ast"],"backgroundTag":"uninspectable-function-source","analyzedSha":"30bb1161095dbae2271e6bc3cc4c219cc3897a57","analyzedAt":"2026-08-28T18:52:54.169Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}