run-llama/llama_index · error · ValueError

Object must be of type {BaseTool}

Error message

Object must be of type {BaseTool}

What it means

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).

Source

Thrown at llama-index-core/llama_index/core/objects/tool_node_mapping.py:42

    tool_identity = (
        f"{tool.metadata.name}{tool.metadata.description}{tool.metadata.fn_schema}"
    )

    return TextNode(
        id_=str(hash(tool_identity)),
        text=node_text,
        metadata={"name": tool.metadata.name},
        excluded_embed_metadata_keys=["name"],
        excluded_llm_metadata_keys=["name"],
    )


class BaseToolNodeMapping(BaseObjectNodeMapping[BaseTool]):
    """Base Tool node mapping."""

    def validate_object(self, obj: BaseTool) -> None:
        if not isinstance(obj, BaseTool):
            raise ValueError(f"Object must be of type {BaseTool}")

    @property
    def obj_node_mapping(self) -> Dict[int, Any]:
        """The mapping data structure between node and object."""
        raise NotImplementedError("Subclasses should implement this!")

    def persist(
        self, persist_dir: str = ..., obj_node_mapping_fname: str = ...
    ) -> None:
        """Persist objs."""
        raise NotImplementedError("Subclasses should implement this!")

    @classmethod
    def from_persist_dir(
        cls,
        persist_dir: str = DEFAULT_PERSIST_DIR,
        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,
    ) -> "BaseToolNodeMapping":

View on GitHub (pinned to afd0fef371)

Solutions

  1. Wrap callables with FunctionTool.from_defaults(fn=...) or QueryEngineTool.from_defaults(...) before adding
  2. Use the matching mapping class for the object type (SimpleToolNodeMapping for BaseTool, SimpleQueryToolNodeMapping for QueryEngineTool)
  3. If dual installations are suspected, ensure a single llama-index-core version in the environment (pip list | grep llama-index)

Example fix

# before
objs = [my_plain_function]
mapping = SimpleToolNodeMapping.from_objects(objs)  # ValueError: Object must be of type {BaseTool}

# after
from llama_index.core.tools import FunctionTool
objs = [FunctionTool.from_defaults(fn=my_plain_function)]
mapping = SimpleToolNodeMapping.from_objects(objs)
Defensive patterns

Strategy: type-guard

Validate before calling

from llama_index.core.tools import BaseTool
if not all(isinstance(o, BaseTool) for o in objs):
    raise TypeError("all objects must be llama_index BaseTool instances; wrap callables with FunctionTool.from_defaults")

Type guard

from llama_index.core.tools import BaseTool
def all_are_base_tools(objs: list) -> bool:
    return all(isinstance(o, BaseTool) for o in objs)

Try / catch

try:
    mapping = SimpleToolNodeMapping.from_objects(objs)
except ValueError as e:
    if "must be of type" in str(e):
        objs = [o if isinstance(o, BaseTool) else FunctionTool.from_defaults(fn=o) for o in objs]
        mapping = SimpleToolNodeMapping.from_objects(objs)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/af8a9e1d1c180c8d. Report an issue: GitHub.