{"record":{"id":"af8a9e1d1c180c8d","repo":"run-llama/llama_index","slug":"object-must-be-of-type-basetool","errorCode":null,"errorMessage":"Object must be of type {BaseTool}","messagePattern":"Object must be of type (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/objects/tool_node_mapping.py","lineNumber":42,"sourceCode":"    tool_identity = (\n        f\"{tool.metadata.name}{tool.metadata.description}{tool.metadata.fn_schema}\"\n    )\n\n    return TextNode(\n        id_=str(hash(tool_identity)),\n        text=node_text,\n        metadata={\"name\": tool.metadata.name},\n        excluded_embed_metadata_keys=[\"name\"],\n        excluded_llm_metadata_keys=[\"name\"],\n    )\n\n\nclass BaseToolNodeMapping(BaseObjectNodeMapping[BaseTool]):\n    \"\"\"Base Tool node mapping.\"\"\"\n\n    def validate_object(self, obj: BaseTool) -> None:\n        if not isinstance(obj, BaseTool):\n            raise ValueError(f\"Object must be of type {BaseTool}\")\n\n    @property\n    def obj_node_mapping(self) -> Dict[int, Any]:\n        \"\"\"The mapping data structure between node and object.\"\"\"\n        raise NotImplementedError(\"Subclasses should implement this!\")\n\n    def persist(\n        self, persist_dir: str = ..., obj_node_mapping_fname: str = ...\n    ) -> None:\n        \"\"\"Persist objs.\"\"\"\n        raise NotImplementedError(\"Subclasses should implement this!\")\n\n    @classmethod\n    def from_persist_dir(\n        cls,\n        persist_dir: str = DEFAULT_PERSIST_DIR,\n        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,\n    ) -> \"BaseToolNodeMapping\":","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/objects/tool_node_mapping.py#L24-L60","documentation":"BaseToolNodeMapping.validate_object enforces that everything added to a tool-based ObjectIndex is an instance of llama_index.core.tools.BaseTool. Passing any other object (plain function, dict, arbitrary class) raises ValueError with the class interpolated via f-string (so the message shows the class object, not the value).","triggerScenarios":"ObjectIndex.from_objects(objs) or SimpleToolNodeMapping.from_objects(objs) where objs contains a plain function, a functools.partial, a dict, or a tool from an incompatible fork/version whose BaseTool class differs.","commonSituations":"Mixing llama-index tool objects with raw Python callables; two llama-index installs (core vs vendored) causing isinstance checks to fail; passing QueryEngineTool where a plain BaseTool mapping expects... or vice versa with the wrong mapping class.","solutions":["Wrap callables with FunctionTool.from_defaults(fn=...) or QueryEngineTool.from_defaults(...) before adding","Use the matching mapping class for the object type (SimpleToolNodeMapping for BaseTool, SimpleQueryToolNodeMapping for QueryEngineTool)","If dual installations are suspected, ensure a single llama-index-core version in the environment (pip list | grep llama-index)"],"exampleFix":"# before\nobjs = [my_plain_function]\nmapping = SimpleToolNodeMapping.from_objects(objs)  # ValueError: Object must be of type {BaseTool}\n\n# after\nfrom llama_index.core.tools import FunctionTool\nobjs = [FunctionTool.from_defaults(fn=my_plain_function)]\nmapping = SimpleToolNodeMapping.from_objects(objs)","handlingStrategy":"type-guard","validationCode":"from llama_index.core.tools import BaseTool\nif not all(isinstance(o, BaseTool) for o in objs):\n    raise TypeError(\"all objects must be llama_index BaseTool instances; wrap callables with FunctionTool.from_defaults\")","typeGuard":"from llama_index.core.tools import BaseTool\ndef all_are_base_tools(objs: list) -> bool:\n    return all(isinstance(o, BaseTool) for o in objs)","tryCatchPattern":"try:\n    mapping = SimpleToolNodeMapping.from_objects(objs)\nexcept ValueError as e:\n    if \"must be of type\" in str(e):\n        objs = [o if isinstance(o, BaseTool) else FunctionTool.from_defaults(fn=o) for o in objs]\n        mapping = SimpleToolNodeMapping.from_objects(objs)\n    else:\n        raise","preventionTips":["Standardize on FunctionTool.from_defaults/QueryEngineTool.from_defaults at creation time","Check for duplicate llama-index installs when isinstance fails unexpectedly"],"tags":["tools","object-index","type-validation"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}