{"record":{"id":"d3275f0ba30d4c3b","repo":"run-llama/llama_index","slug":"metadata-must-be-set-d3275f","errorCode":null,"errorMessage":"Metadata must be set","messagePattern":"Metadata must be set","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/objects/tool_node_mapping.py","lineNumber":95,"sourceCode":"        self._tools = {tool.metadata.name: tool for tool in objs}\n\n    @classmethod\n    def from_objects(\n        cls, objs: Sequence[BaseTool], *args: Any, **kwargs: Any\n    ) -> \"BaseObjectNodeMapping\":\n        return cls(objs)\n\n    def _add_object(self, tool: BaseTool) -> None:\n        self._tools[tool.metadata.name] = tool\n\n    def to_node(self, tool: BaseTool) -> TextNode:\n        \"\"\"To node.\"\"\"\n        return convert_tool_to_node(tool)\n\n    def _from_node(self, node: BaseNode) -> BaseTool:\n        \"\"\"From node.\"\"\"\n        if node.metadata is None:\n            raise ValueError(\"Metadata must be set\")\n        return self._tools[node.metadata[\"name\"]]\n\n\nclass BaseQueryToolNodeMapping(BaseObjectNodeMapping[QueryEngineTool]):\n    \"\"\"Base query tool node mapping.\"\"\"\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    ) -> \"BaseQueryToolNodeMapping\":\n        raise NotImplementedError(\n            \"This object node mapping does not support persist method.\"\n        )\n\n    @property\n    def obj_node_mapping(self) -> Dict[int, Any]:","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/objects/tool_node_mapping.py#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom retrieval code that creates TextNodes manually; docstore entries from older versions; tests feeding bare TextNodes into from_node.","solutions":["Create nodes via the mapping (mapping.to_node(tool)) or convert_tool_to_node so metadata['name'] is populated","Before from_node, check `if not node.metadata or 'name' not in node.metadata: raise/skip`","Ensure transformations don't drop the 'name' metadata key from tool nodes"],"exampleFix":"# before\nnode = TextNode(text=\"tool: search\")  # no metadata\ntool = mapping.from_node(node)  # ValueError: Metadata must be set\n\n# after\nnode = mapping.to_node(tool)  # metadata={'name': 'search', ...}\nresolved = mapping.from_node(node)","handlingStrategy":"validation","validationCode":"if not node.metadata or \"name\" not in node.metadata:\n    raise ValueError(f\"node {node.id_} missing metadata['name']\")\nif node.metadata[\"name\"] not in mapping.obj_node_mapping if hasattr(mapping, 'obj_node_mapping') else True:\n    pass  # name not pre-registered; from_node would KeyError\nresolved = mapping.from_node(node)","typeGuard":"def node_has_tool_name(node) -> bool:\n    return bool(node.metadata) and isinstance(node.metadata.get(\"name\"), str)","tryCatchPattern":"try:\n    tool = mapping.from_node(node)\nexcept ValueError as e:\n    if \"Metadata must be set\" in str(e):\n        log_and_skip(node)\n    else:\n        raise\nexcept KeyError:\n    log_and_skip(node)  # name not in tool dict","preventionTips":["Create tool nodes via convert_tool_to_node/mapping.to_node only","Avoid transformations that drop the 'name' metadata key"],"tags":["tools","node-mapping","metadata","validation"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}