agentscope-ai/agentscope · error · TypeError

{type(self).__name__} must be called with keyword arguments

Error message

{type(self).__name__} must be called with keyword arguments only, but got {len(args)} positional argument(s).

What it means

Tool.__call__ enforces keyword-only invocation: any positional arguments raise TypeError with their count. Tool arguments must be passed by name so they can be mapped to the tool schema.

Source

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

        **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
        subclasses that override ``__call__`` with their own positional
        parameters; any positional argument actually passed here is rejected
        (raising :exc:`TypeError`) so it fails loudly instead of being silently
        dropped.

        Middlewares are applied in an onion fashion: the first registered
        middleware is the outermost layer and runs its pre-logic before
        any inner layers, then its post-logic after all inner layers
        have completed.
        """
        if args:
            raise TypeError(
                f"{type(self).__name__} must be called with keyword arguments "
                f"only, but got {len(args)} positional argument(s).",
            )
        # ``getattr`` with a default so the no-middleware path keeps working
        # even if a subclass overrides ``__init__`` without calling
        # ``super().__init__()``.
        middlewares = getattr(self, "_middlewares", [])
        if not middlewares:
            if inspect.isasyncgenfunction(self.call):
                return self.call(**kwargs)
            return await self.call(**kwargs)

        async def execute_chain(
            index: int = 0,
            **chain_kwargs: Any,
        ) -> AsyncGenerator[ToolChunk, None]:
            """Execute the tool middleware chain."""
            if index >= len(middlewares):

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Pass every argument by keyword: my_tool(query='hello')
  2. Change dynamic invocation to **kwargs: my_tool(**params_dict)
  3. Update tests/call sites after wrapping functions as tools

Example fix

# before
await my_tool('hello', 5)
# after
await my_tool(query='hello', limit=5)
Defensive patterns

Strategy: validation

Validate before calling

assert not args, 'tools accept keyword arguments only'

Try / catch

try:
    await tool(*args)
except TypeError as e:
    if 'keyword arguments' in str(e):
        await tool(**dict(zip(param_names, args)))
    else: raise

Prevention

When it happens

Trigger: my_tool('hello') or my_tool('a', 'b') instead of my_tool(query='hello'); also *args unpacking of a tuple instead of **kwargs of a dict.

Common situations: Refactoring from a plain function to a Tool wrapper and keeping positional call sites; dynamic invocation using *args instead of **kwargs.

Related errors


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