{"record":{"id":"5a6ff421b04aef2a","repo":"run-llama/llama_index","slug":"node-must-have-a-tool-name-in-metadata","errorCode":null,"errorMessage":"Node must have a tool_name in metadata.","messagePattern":"Node must have a tool_name in metadata\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/query_engine/router_query_engine.py","lineNumber":260,"sourceCode":"            # add selected result\n            final_response.metadata = final_response.metadata or {}\n            final_response.metadata[\"selector_result\"] = result\n\n            query_event.on_end(payload={EventPayload.RESPONSE: final_response})\n\n        return final_response\n\n\ndef default_node_to_metadata_fn(node: BaseNode) -> ToolMetadata:\n    \"\"\"\n    Default node to metadata function.\n\n    We use the node's text as the Tool description.\n\n    \"\"\"\n    metadata = node.metadata or {}\n    if \"tool_name\" not in metadata:\n        raise ValueError(\"Node must have a tool_name in metadata.\")\n    return ToolMetadata(name=metadata[\"tool_name\"], description=node.get_content())\n\n\nclass RetrieverRouterQueryEngine(BaseQueryEngine):\n    \"\"\"\n    Retriever-based router query engine.\n\n    NOTE: this is deprecated, please use our new ToolRetrieverRouterQueryEngine\n\n    Use a retriever to select a set of Nodes. Each node will be converted\n    into a ToolMetadata object, and also used to retrieve a query engine, to form\n    a QueryEngineTool.\n\n    NOTE: this is a beta feature. We are figuring out the right interface\n    between the retriever and query engine.\n\n    Args:\n        selector (BaseSelector): A selector that chooses one out of many options based","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/query_engine/router_query_engine.py#L242-L278","documentation":"default_node_to_metadata_fn converts a retrieved Node into ToolMetadata for the deprecated RetrieverRouterQueryEngine. It requires the node metadata dict to contain a 'tool_name' key, which becomes the tool name; the node text becomes the description. Missing key raises ValueError.","triggerScenarios":"Building RetrieverRouterQueryEngine over an index whose nodes lack metadata['tool_name'] while using the default node_to_query_engine_fn/metadata conversion (i.e. not supplying a custom node_to_metadata_fn or custom node_to_query_engine_fn).","commonSituations":"Indexing documents without per-node metadata, or migrating from an old workflow where tool_name was injected into node metadata. The class itself is deprecated in favor of ToolRetrieverRouterQueryEngine.","solutions":["Add metadata={\"tool_name\": \"...\"} to each node/document before indexing so default_node_to_metadata_fn can map it","Pass a custom node_to_metadata_fn to RetrieverRouterQueryEngine that derives a tool name without requiring 'tool_name'","Migrate to ToolRetrieverRouterQueryEngine, which routes over QueryEngineTool objects and does not need node metadata"],"exampleFix":"// before\ndoc = Document(text=\"Handles billing questions\")\nindex = SummaryIndex.from_documents([doc])\nrouter = RetrieverRouterQueryEngine(retriever=index.as_retriever(), node_to_query_engine_fn=fn)\n\n// after\ndoc = Document(\n    text=\"Handles billing questions\",\n    metadata={\"tool_name\": \"billing_engine\"},\n)\nindex = SummaryIndex.from_documents([doc])\nrouter = RetrieverRouterQueryEngine(retriever=index.as_retriever(), node_to_query_engine_fn=fn)","handlingStrategy":"validation","validationCode":"def node_has_tool_name(node) -> bool:\n    return bool((node.metadata or {}).get(\"tool_name\"))\n\nassert all(node_has_tool_name(n) for n in routing_index.docstore.docs.values()), \\\n    \"every routing node needs metadata['tool_name']\"","typeGuard":"from llama_index.core.schema import BaseNode\n\ndef is_routable_node(node: BaseNode) -> bool:\n    \"\"\"Node can be converted by default_node_to_metadata_fn.\"\"\"\n    return \"tool_name\" in (node.metadata or {})","tryCatchPattern":null,"preventionTips":["Inject metadata={'tool_name': ...} at Document creation for routing indexes","Prefer the non-deprecated ToolRetrieverRouterQueryEngine over RetrieverRouterQueryEngine","Supply a custom node_to_metadata_fn when nodes cannot carry tool_name"],"tags":["router","metadata","deprecated","nodes"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}