huggingface/smolagents · error · ValueError

No function definition found in the provided source of {tool

Error message

No function definition found in the provided source of {tool_function.__name__}. Ensure the input is a standard function.

What it means

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.

Source

Thrown at src/smolagents/tools.py:1121

    # Get the signature parameters of the tool function
    sig = inspect.signature(tool_function)
    # - Add "self" as first parameter to tool_function signature
    new_sig = sig.replace(
        parameters=[inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD)] + list(sig.parameters.values())
    )
    # - Set the signature of the forward method
    SimpleTool.forward.__signature__ = new_sig

    # Create and attach the source code of the dynamically created tool class and forward method
    # - Get the source code of tool_function
    tool_source = textwrap.dedent(inspect.getsource(tool_function))
    # - Remove the tool decorator and function definition line
    lines = tool_source.splitlines()
    tree = ast.parse(tool_source)
    #   - Find function definition
    func_node = next((node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)), None)
    if not func_node:
        raise ValueError(
            f"No function definition found in the provided source of {tool_function.__name__}. "
            "Ensure the input is a standard function."
        )
    #   - Extract decorator lines
    decorator_lines = ""
    if func_node.decorator_list:
        tool_decorators = [d for d in func_node.decorator_list if isinstance(d, ast.Name) and d.id == "tool"]
        if len(tool_decorators) > 1:
            raise ValueError(
                f"Multiple @tool decorators found on function '{func_node.name}'. Only one @tool decorator is allowed."
            )
        if len(tool_decorators) < len(func_node.decorator_list):
            warnings.warn(
                f"Function '{func_node.name}' has decorators other than @tool. "
                "This may cause issues with serialization in the remote executor. See issue #1626."
            )
        decorator_start = tool_decorators[0].end_lineno if tool_decorators else 0
        decorator_end = func_node.decorator_list[-1].end_lineno

View on GitHub (pinned to 30bb116109)

Solutions

  1. Convert the callable to a standard `def` function at module level and decorate that
  2. Define tools in real .py files (not notebooks/exec/dynamic strings) so inspect.getsource works
  3. Avoid wrapping lambdas, partials, or builtins with @tool; write an explicit wrapper function

Example fix

# before
get_weather = tool(lambda city: fetch(city))
# after
@tool
def get_weather(city: str) -> str:
    return fetch(city)
Defensive patterns

Strategy: type-guard

Validate before calling

import inspect, ast
def has_function_def_source(func) -> bool:
    try:
        tree = ast.parse(inspect.getsource(func))
    except (TypeError, OSError):
        return False
    return any(isinstance(n, ast.FunctionDef) for n in ast.walk(tree))

assert has_function_def_source(candidate), "define tool as a plain def in a .py file"

Type guard

def is_wrappable_as_tool(func) -> bool:
    return inspect.isfunction(func) and not inspect.isbuiltin(func) and has_function_def_source(func)

Try / catch

try:
    my_tool = tool(candidate)
except ValueError as e:
    if "No function definition" in str(e):
        def wrapper(arg: str) -> str:
            return candidate(arg)
        wrapper.__name__ = getattr(candidate, '__name__', 'tool_fn')
        my_tool = tool(wrapper)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28). Data as JSON: /api/errors/d089a2b45c3f135e. Report an issue: GitHub.