huggingface/smolagents · error · NotImplementedError

Write this method in your subclass of `Tool`.

Error message

Write this method in your subclass of `Tool`.

What it means

The base Tool.forward is abstract and raises NotImplementedError; every usable tool must override it. __call__ delegates to forward, so calling an un-subclassed or partially implemented Tool fails here.

Source

Thrown at src/smolagents/tools.py:229

            json_schema = _convert_type_hints_to_json_schema(self.forward, error_on_missing_type_hints=False)[
                "properties"
            ]  # This function will not raise an error on missing docstrings, contrary to get_json_schema
            for key, value in self.inputs.items():
                assert key in json_schema, (
                    f"Input '{key}' should be present in function signature, found only {json_schema.keys()}"
                )
                if "nullable" in value:
                    assert "nullable" in json_schema[key], (
                        f"Nullable argument '{key}' in inputs should have key 'nullable' set to True in function signature."
                    )
                if key in json_schema and "nullable" in json_schema[key]:
                    assert "nullable" in value, (
                        f"Nullable argument '{key}' in function signature should have key 'nullable' set to True in inputs."
                    )

    def forward(self, *args, **kwargs):
        raise NotImplementedError("Write this method in your subclass of `Tool`.")

    def __call__(self, *args, sanitize_inputs_outputs: bool = False, **kwargs):
        if not self.is_initialized:
            self.setup()

        # Handle the arguments might be passed as a single dictionary
        if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], dict):
            potential_kwargs = args[0]

            # If the dictionary keys match our input parameters, convert it to kwargs
            if all(key in self.inputs for key in potential_kwargs):
                args = ()
                kwargs = potential_kwargs

        if sanitize_inputs_outputs:
            args, kwargs = handle_agent_input_types(*args, **kwargs)
        outputs = self.forward(*args, **kwargs)
        if sanitize_inputs_outputs:

View on GitHub (pinned to 30bb116109)

Solutions

  1. Implement def forward(self, ...) in your Tool subclass with parameters matching self.inputs
  2. Subclass a more specific base (PipelineTool) which splits work into setup/forward_inputs/forward_output, or use the @tool decorator
  3. Check for typos in the method name (it must be exactly 'forward')

Example fix

# before
class MyTool(Tool):
    def run(self, query): return query
# after
class MyTool(Tool):
    def forward(self, query: str) -> str: return query
Defensive patterns

Strategy: type-guard

Validate before calling

import inspect
def forward_implemented(cls) -> bool:
    return cls.forward is not Tool.forward

Type guard

def is_callable_tool(tool) -> bool:
    return type(tool).forward is not Tool.forward

Try / catch

try:
    result = tool("input")
except NotImplementedError:
    logger.error("%s is incomplete: implement forward()", type(tool).__name__)

Prevention

When it happens

Trigger: Instantiating Tool directly and calling it; subclassing Tool but naming the method 'run', '_forward', or misspelling 'forward'; forgetting to implement forward in a custom tool class.

Common situations: Following a tutorial and skipping the forward implementation; renaming methods during refactor; creating a Tool subclass skeleton to test setup() only.

Related errors


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