run-llama/llama_index · error · ValueError

Tool name cannot be None

Error message

Tool name cannot be None

What it means

LoadAndSearchToolSpec's __init__ requires ToolMetadata with a non-None name because it derives its two function names ('<name>' and 'read_<name>') from it. Passing ToolMetadata(name=None) (the default when name is omitted in some constructors) fails immediately at construction time.

Source

Thrown at llama-index-core/llama_index/core/tools/tool_spec/load_and_search/base.py:65

    """

    def __init__(
        self,
        tool: FunctionTool,
        index_cls: Type[BaseIndex],
        index_kwargs: Dict,
        metadata: ToolMetadata,
        index: Optional[BaseIndex] = None,
    ) -> None:
        """Init params."""
        self._index_cls = index_cls
        self._index_kwargs = index_kwargs
        self._index = index
        self._metadata = metadata
        self._tool = tool

        if self._metadata.name is None:
            raise ValueError("Tool name cannot be None")
        self.spec_functions = [
            self._metadata.name,
            f"read_{self._metadata.name}",
        ]
        self._tool_list = [
            FunctionTool.from_defaults(
                fn=self.load,
                name=self._metadata.name,
                description=self.loader_prompt.format(
                    self._metadata.name, self._metadata.description
                ),
                fn_schema=self._metadata.fn_schema,
            ),
            FunctionTool.from_defaults(
                fn=self.read,
                name=str(f"read_{self._metadata.name}"),
                description=self.reader_prompt.format(metadata.name),
                fn_schema=create_schema_from_function("ReadData", self.read),

View on GitHub (pinned to afd0fef371)

Solutions

  1. Set an explicit name: metadata = ToolMetadata(name='my_doc_tool', description='...').
  2. If wrapping another tool, reuse its name: ToolMetadata(name=inner_tool.metadata.name, ...).
  3. Sanitize the name to ^[a-zA-Z0-9_-]+$ since it is also used in generated function names.

Example fix

# before
spec = LoadAndSearchToolSpec(
    tool=doc_tool, index_cls=VectorStoreIndex, index_kwargs={},
    metadata=ToolMetadata(description='searches docs'),  # name is None
)

# after
spec = LoadAndSearchToolSpec(
    tool=doc_tool, index_cls=VectorStoreIndex, index_kwargs={},
    metadata=ToolMetadata(name='doc_search', description='searches docs'),
)
Defensive patterns

Strategy: validation

Validate before calling

from llama_index.core.tools import ToolMetadata

def make_load_search_spec(tool, index_cls, index_kwargs):
    meta = tool.metadata if hasattr(tool, 'metadata') else None
    name = getattr(meta, 'name', None) or 'doc_tool'
    return ToolMetadata(name=name, description=meta.description if meta else '')

Type guard

def has_tool_name(metadata) -> bool:
    return getattr(metadata, 'name', None) is not None

Try / catch

try:
    spec = LoadAndSearchToolSpec(tool=t, index_cls=VectorStoreIndex,
                                index_kwargs={}, metadata=meta)
except ValueError as e:
    if 'Tool name cannot be None' in str(e):
        meta = meta.model_copy(update={'name': 'doc_tool'})
        spec = LoadAndSearchToolSpec(tool=t, index_cls=VectorStoreIndex,
                                    index_kwargs={}, metadata=meta)
    else:
        raise

Prevention

When it happens

Trigger: Constructing LoadAndSearchToolSpec(tool=..., index_cls=..., index_kwargs=..., metadata=ToolMetadata(description='...')) with no name; or building ToolMetadata programmatically where name was never set.

Common situations: Creating a load-and-search tool over a document tool (e.g. from llama_index.core.tools.tool_spec.load_and_search) and forgetting that ToolMetadata.name is Optional in the type system but mandatory for this spec.

Related errors


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