run-llama/llama_index · error · ValueError

Metadata must be set

Error message

Metadata must be set

What it means

SimpleToolNodeMapping._from_node resolves a retrieved node back to the original tool by looking up node.metadata['name'] in an in-memory dict. It first requires node.metadata to be non-None and raises ValueError('Metadata must be set') otherwise; note that even with metadata set, a missing 'name' key will then raise KeyError.

Source

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

        self._tools = {tool.metadata.name: tool for tool in objs}

    @classmethod
    def from_objects(
        cls, objs: Sequence[BaseTool], *args: Any, **kwargs: Any
    ) -> "BaseObjectNodeMapping":
        return cls(objs)

    def _add_object(self, tool: BaseTool) -> None:
        self._tools[tool.metadata.name] = tool

    def to_node(self, tool: BaseTool) -> TextNode:
        """To node."""
        return convert_tool_to_node(tool)

    def _from_node(self, node: BaseNode) -> BaseTool:
        """From node."""
        if node.metadata is None:
            raise ValueError("Metadata must be set")
        return self._tools[node.metadata["name"]]


class BaseQueryToolNodeMapping(BaseObjectNodeMapping[QueryEngineTool]):
    """Base query tool node mapping."""

    @classmethod
    def from_persist_dir(
        cls,
        persist_dir: str = DEFAULT_PERSIST_DIR,
        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,
    ) -> "BaseQueryToolNodeMapping":
        raise NotImplementedError(
            "This object node mapping does not support persist method."
        )

    @property
    def obj_node_mapping(self) -> Dict[int, Any]:

View on GitHub (pinned to afd0fef371)

Solutions

  1. Create nodes via the mapping (mapping.to_node(tool)) or convert_tool_to_node so metadata['name'] is populated
  2. Before from_node, check `if not node.metadata or 'name' not in node.metadata: raise/skip`
  3. Ensure transformations don't drop the 'name' metadata key from tool nodes

Example fix

# before
node = TextNode(text="tool: search")  # no metadata
tool = mapping.from_node(node)  # ValueError: Metadata must be set

# after
node = mapping.to_node(tool)  # metadata={'name': 'search', ...}
resolved = mapping.from_node(node)
Defensive patterns

Strategy: validation

Validate before calling

if not node.metadata or "name" not in node.metadata:
    raise ValueError(f"node {node.id_} missing metadata['name']")
if node.metadata["name"] not in mapping.obj_node_mapping if hasattr(mapping, 'obj_node_mapping') else True:
    pass  # name not pre-registered; from_node would KeyError
resolved = mapping.from_node(node)

Type guard

def node_has_tool_name(node) -> bool:
    return bool(node.metadata) and isinstance(node.metadata.get("name"), str)

Try / catch

try:
    tool = mapping.from_node(node)
except ValueError as e:
    if "Metadata must be set" in str(e):
        log_and_skip(node)
    else:
        raise
except KeyError:
    log_and_skip(node)  # name not in tool dict

Prevention

When it happens

Trigger: Calling mapping.from_node(node) during ObjectIndex/retriever resolution with a node that was constructed without metadata, a node whose metadata was cleared (excluded keys stripped incorrectly), or a synthetic test node.

Common situations: Custom retrieval code that creates TextNodes manually; docstore entries from older versions; tests feeding bare TextNodes into from_node.

Related errors


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