{"record":{"id":"a295d9da8641dc91","repo":"Fosowl/agenticSeek","slug":"tool-must-be-a-callable-object-a-method","errorCode":null,"errorMessage":"Tool must be a callable object (a method)","messagePattern":"Tool must be a callable object \\(a method\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"sources/agents/agent.py","lineNumber":94,"sourceCode":"    \n    @property\n    def get_status_message(self) -> str:\n        return self.status_message\n\n    @property\n    def get_tools(self) -> dict:\n        return self.tools\n    \n    @property\n    def get_success(self) -> bool:\n        return self.success\n    \n    def get_blocks_result(self) -> list:\n        return self.blocks_result\n\n    def add_tool(self, name: str, tool: Callable) -> None:\n        if not callable(tool):\n            raise TypeError(\"Tool must be a callable object (a method)\")\n        self.tools[name] = tool\n    \n    def get_tools_name(self) -> list:\n        \"\"\"\n        Get the list of tools names.\n        \"\"\"\n        return list(self.tools.keys())\n    \n    def get_tools_description(self) -> str:\n        \"\"\"\n        Get the list of tools names and their description.\n        \"\"\"\n        description = \"\"\n        for name in self.get_tools_name():\n            description += f\"{name}: {self.tools[name].description}\\n\"\n        return description\n    \n    def load_prompt(self, file_path: str) -> str:","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/Fosowl/agenticSeek/blob/ae57a2357745a9706cb12d0fd76d954c84d166fa/sources/agents/agent.py#L76-L112","documentation":"add_tool() validates that the value passed as `tool` is callable before storing it in self.tools; otherwise later agent execution would crash when invoking it. It raises TypeError immediately at registration time to fail fast. Only `callable()` is checked — the `name` key itself is not validated.","triggerScenarios":"Calling agent.add_tool(name, tool) where tool is not callable, e.g. a string, dict, None, or the result of calling a function instead of the function itself (tool() vs tool). As the test names show, non-callables are rejected intentionally.","commonSituations":"Passing a function's return value instead of the function (missing @ or extra parentheses); passing a tool described as a dict/JSON spec rather than a Python callable; passing a method name string; typos leaving the variable None.","solutions":["Pass the callable itself, not a call result: add_tool(\"search\", search_tool) not add_tool(\"search\", search_tool()).","If wrapping, pass a lambda/partial: add_tool(\"search\", lambda q: run_search(q)).","Print type(tool) before the call to confirm it is a function/method/functor.","If you only have a spec/dict, wrap it in a real function before registering."],"exampleFix":"// before\nagent.add_tool(\"search\", search_tool())  # TypeError\n// after\nagent.add_tool(\"search\", search_tool)","handlingStrategy":"type-guard","validationCode":"def safe_add_tool(agent, name, tool):\n    if not callable(tool):\n        raise TypeError(f\"{name!r} must be a callable, got {type(tool).__name__}\")\n    agent.add_tool(name, tool)","typeGuard":"def is_callable_tool(tool) -> bool:\n    return callable(tool)  # functions, methods, lambdas, functors with __call__","tryCatchPattern":"try:\n    agent.add_tool(name, tool)\nexcept TypeError as e:\n    if \"must be a callable\" in str(e):\n        raise ValueError(f\"Tool '{name}' is not callable; pass the function itself, not its result\") from e\n    raise","preventionTips":["Never write add_tool(\"x\", fn()) — parentheses invoke; pass fn itself.","If a tool needs arguments, register a lambda or functools.partial instead of the bound result.","Add a unit test mirroring test_add_tool_rejects_non_callable for each tool registry helper.","Log type(tool) at registration sites during development to catch accidental call results."],"tags":["python","type-error","api-misuse","tools"],"backgroundTag":"invalid-tool-type","analyzedSha":"ae57a2357745a9706cb12d0fd76d954c84d166fa","analyzedAt":"2026-08-30T02:49:05.834Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}