huggingface/smolagents · warning · UserWarning
Function '{func_node.name}' has decorators other than @tool.
Error message
Function '{func_node.name}' has decorators other than @tool. This may cause issues with serialization in the remote executor. See issue #1626. What it means
The `@tool` decorator serializes your function's source code (it strips/reconstructs decorators) so it can be shipped to a remote code executor. If the function has decorators beyond a single `@tool`, they cannot be serialized faithfully, and smolagents warns that this may break remote execution (GitHub issue #1626).
Source
Thrown at src/smolagents/tools.py:1134
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
decorator_lines = "\n".join(lines[decorator_start:decorator_end])
# - Extract tool source body
body_start = func_node.body[0].lineno - 1 # AST lineno starts at 1
tool_source_body = "\n".join(lines[body_start:])
# - Create the forward method source, including def line and indentation
forward_method_source = f"def forward{new_sig}:\n{tool_source_body}"
# - Create the class source
indent = " " * 4 # for class method
class_source = (
textwrap.dedent(f"""
class SimpleTool(Tool):
name: str = "{tool_json_schema["name"]}"
description: str = {json.dumps(textwrap.dedent(tool_json_schema["description"]).strip())}View on GitHub (pinned to 30bb116109)
Solutions
- Remove the extra decorators from the @tool function; move caching/wrapping inside the function body
- Apply the other decorator OUTSIDE @tool only if remote execution isn't needed, otherwise restructure: write an undecorated function, decorate it with @tool, and cache at call sites
- If you don't use remote/egress code execution, the warning can be ignored, but reordering so @tool is outermost avoids the serialization issue
Example fix
# before
@tool
@lru_cache(maxsize=None)
def fetch_price(ticker: str) -> str:
...
# after
def _fetch_price(ticker: str) -> str: ...
@tool
def fetch_price(ticker: str) -> str:
return _fetch_price(ticker) Defensive patterns
Strategy: validation
Validate before calling
import ast, inspect
def has_only_tool_decorator(func) -> bool:
src = inspect.getsource(func.__wrapped__ if hasattr(func, '__wrapped__') else func)
tree = ast.parse(textwrap.dedent(src))
fn = tree.body[0]
return all(isinstance(d, ast.Name) and d.id == 'tool' for d in fn.decorator_list) Prevention
- Keep @tool functions free of other decorators; implement caching/wrapping inside the body
- If stacking is unavoidable, apply other decorators outside @tool and skip remote execution for that tool
- Add a unit test asserting each registered tool serializes cleanly for the remote executor
When it happens
Trigger: Applying `@tool` to a function that also has other decorators, e.g. `@functools.lru_cache`, `@staticmethod`, `@some_decorator` stacked with `@tool`. The AST check `len(tool_decorators) < len(func_node.decorator_list)` fires the warning during `tool()` decoration.
Common situations: Decorating cached or wrapped helper functions with `@tool`; using `@tool` on methods inside classes with additional decorators; decorator order like `@lru_cache` above `@tool` causing wrong serialization for egress-executed tools.
Related errors
- Unknown model class '{model_info['class']}'. Supported model
- Unsupported comparison operator: {op}
- {expression.__class__.__name__} is not supported.
- Cannot serialize object: {e}
- Pickle data rejected: allow_pickle=False requires safe-only
AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28).
Data as JSON: /api/errors/cd5bedc6d3e67d25.
Report an issue: GitHub.