{"record":{"id":"368268fe3d9a5708","repo":"agentscope-ai/agentscope","slug":"self-class-name-does-not-implement-call","errorCode":null,"errorMessage":"{self.__class__.__name__} does not implement call","messagePattern":"(.+?) does not implement call","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"src/agentscope/tool/_base.py","lineNumber":166,"sourceCode":"    ) -> ToolChunk | AsyncGenerator[ToolChunk, None]:\n        \"\"\"Execute the tool logic.\n\n        This is the new override point for tool implementations.\n        Subclasses should override this method instead of\n        :meth:`__call__`.  The base implementation raises\n        :exc:`NotImplementedError` for non-external tools and\n        :exc:`RuntimeError` for external tools.\n\n        Args:\n            **kwargs: Tool input arguments.\n\n        Returns:\n            `ToolChunk | AsyncGenerator[ToolChunk, None]`:\n                A single :class:`~agentscope.tool.ToolChunk` or an\n                async generator that yields them.\n        \"\"\"\n        if not self.is_external_tool:\n            raise NotImplementedError(\n                f\"{self.__class__.__name__} does not implement call\",\n            )\n\n        raise RuntimeError(\n            f\"{self.__class__.__name__} is an external tool and should not \"\n            f\"be called directly\",\n        )\n\n    async def __call__(\n        self,\n        *args: Any,\n        **kwargs: Any,\n    ) -> ToolChunk | AsyncGenerator[ToolChunk, None]:\n        \"\"\"Invoke the tool, layering any registered middlewares around\n        :meth:`call`.\n\n        Tools are always invoked with keyword arguments only. ``*args`` is\n        accepted in the signature solely to stay Liskov-compatible with","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/tool/_base.py#L148-L184","documentation":"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.","triggerScenarios":"Defining a Tool subclass without implementing async call(), then invoking it (directly or via __call__/execute_chain in a toolkit run).","commonSituations":"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.","solutions":["Implement `async def call(...)` in your subclass returning ToolChunk or an async generator of them","Check the exact expected signature in sibling builtin tools","If it's an external (MCP) tool, mark it accordingly instead of implementing call"],"exampleFix":"# before\nclass MyTool(ToolBase):\n    async def run(self, x): ...\n# after\nclass MyTool(ToolBase):\n    async def call(self, x) -> ToolChunk: ...","handlingStrategy":"validation","validationCode":"import inspect\nassert ToolBase.call is not type(tool).call, f'{type(tool).__name__} must implement call()'","typeGuard":"def tool_implements_call(tool) -> bool:\n    return type(tool).call is not ToolBase.call","tryCatchPattern":"try:\n    await tool(**kwargs)\nexcept NotImplementedError as e:\n    raise TypeError(f'misconfigured tool: {e}') from e","preventionTips":["Always implement async call() in custom Tool subclasses","Add a smoke-test that calls each registered tool once"],"tags":["abstract-method","tool","subclassing"],"backgroundTag":"abstract-method-not-implemented","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}