agentscope-ai/agentscope · error · NotImplementedError

{self.__class__.__name__} does not implement call

Error message

{self.__class__.__name__} does not implement call

What it means

ToolBase.call() is abstract; subclasses that are not external tools must override it. Calling call() on a subclass that didn't override it raises NotImplementedError, naming the class.

Source

Thrown at src/agentscope/tool/_base.py:166

    ) -> ToolChunk | AsyncGenerator[ToolChunk, None]:
        """Execute the tool logic.

        This is the new override point for tool implementations.
        Subclasses should override this method instead of
        :meth:`__call__`.  The base implementation raises
        :exc:`NotImplementedError` for non-external tools and
        :exc:`RuntimeError` for external tools.

        Args:
            **kwargs: Tool input arguments.

        Returns:
            `ToolChunk | AsyncGenerator[ToolChunk, None]`:
                A single :class:`~agentscope.tool.ToolChunk` or an
                async generator that yields them.
        """
        if not self.is_external_tool:
            raise NotImplementedError(
                f"{self.__class__.__name__} does not implement call",
            )

        raise RuntimeError(
            f"{self.__class__.__name__} is an external tool and should not "
            f"be called directly",
        )

    async def __call__(
        self,
        *args: Any,
        **kwargs: Any,
    ) -> ToolChunk | AsyncGenerator[ToolChunk, None]:
        """Invoke the tool, layering any registered middlewares around
        :meth:`call`.

        Tools are always invoked with keyword arguments only. ``*args`` is
        accepted in the signature solely to stay Liskov-compatible with

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Implement `async def call(...)` in your subclass returning ToolChunk or an async generator of them
  2. Check the exact expected signature in sibling builtin tools
  3. If it's an external (MCP) tool, mark it accordingly instead of implementing call

Example fix

# before
class MyTool(ToolBase):
    async def run(self, x): ...
# after
class MyTool(ToolBase):
    async def call(self, x) -> ToolChunk: ...
Defensive patterns

Strategy: validation

Validate before calling

import inspect
assert ToolBase.call is not type(tool).call, f'{type(tool).__name__} must implement call()'

Type guard

def tool_implements_call(tool) -> bool:
    return type(tool).call is not ToolBase.call

Try / catch

try:
    await tool(**kwargs)
except NotImplementedError as e:
    raise TypeError(f'misconfigured tool: {e}') from e

Prevention

When it happens

Trigger: Defining a Tool subclass without implementing async call(), then invoking it (directly or via __call__/execute_chain in a toolkit run).

Common situations: Forgetting to implement call when creating a custom tool; renaming the method (e.g. run) so the abstract call is never overridden; instantiation succeeds and the failure only appears at invocation time.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/368268fe3d9a5708. Report an issue: GitHub.