run-llama/llama_index · error · ValueError

Retriever and query engine ids must not overlap.

Error message

Retriever and query engine ids must not overlap.

What it means

RecursiveRetriever stores retriever_dict and query_engine_dict in one namespace: a QueryBundle id looked up during recursion is checked against retrievers first, then query engines, so an id present in both is ambiguous. __init__ therefore raises ValueError when the two dicts share any key (set intersection is non-empty).

Source

Thrown at llama-index-core/llama_index/core/retrievers/recursive_retriever.py:63

        query_engine_dict: Optional[Dict[str, BaseQueryEngine]] = None,
        node_dict: Optional[Dict[str, BaseNode]] = None,
        callback_manager: Optional[CallbackManager] = None,
        query_response_tmpl: Optional[str] = None,
        verbose: bool = False,
    ) -> None:
        """Init params."""
        self._root_id = root_id
        if root_id not in retriever_dict:
            raise ValueError(
                f"Root id {root_id} not in retriever_dict, it must be a retriever."
            )
        self._retriever_dict = retriever_dict
        self._query_engine_dict = query_engine_dict or {}
        self._node_dict = node_dict or {}

        # make sure keys don't overlap
        if set(self._retriever_dict.keys()) & set(self._query_engine_dict.keys()):
            raise ValueError("Retriever and query engine ids must not overlap.")

        self._query_response_tmpl = query_response_tmpl or DEFAULT_QUERY_RESPONSE_TMPL
        super().__init__(callback_manager, verbose=verbose)

    def _deduplicate_nodes(
        self, nodes_with_score: List[NodeWithScore]
    ) -> List[NodeWithScore]:
        """
        Deduplicate nodes according to node id.
        Keep the node with the highest score/first returned.
        """
        node_ids = set()
        deduplicate_nodes = []
        for node_with_score in nodes_with_score:
            node = node_with_score.node
            if node.id_ not in node_ids:
                node_ids.add(node.id_)
                deduplicate_nodes.append(node_with_score)

View on GitHub (pinned to afd0fef371)

Solutions

  1. Rename the colliding keys so every id is unique across both dicts, and update the corresponding IndexNode references (the node's index_id must match the new key).
  2. Generate ids from one source of truth and assert uniqueness before constructing the retriever.
  3. If you need both interfaces for one sub-index, suffix them: 'chunks_retriever' / 'chunks_engine'.

Example fix

# before
retriever = RecursiveRetriever(
    "root",
    retriever_dict={"data": retriever},
    query_engine_dict={"data": engine},
)

# after
retriever = RecursiveRetriever(
    "root",
    retriever_dict={"data": retriever},
    query_engine_dict={"data_engine": engine},
)
# and point the corresponding IndexNode at "data_engine"
Defensive patterns

Strategy: validation

Validate before calling

overlap = set(retriever_dict) & set(query_engine_dict or {})
assert not overlap, f"id collision between dicts: {overlap}"

Prevention

When it happens

Trigger: Building RecursiveRetriever with the same string (e.g. 'chunks') used both as a retriever id and a query engine id; generating ids for IndexNodes programmatically and accidentally registering the same id for a sub-index retriever and a sub-index query engine; copy-paste between the two dicts.

Common situations: Compositional pipelines where each sub-index is exposed both ways; loops creating {'id': index.as_retriever()} and {'id': index.as_query_engine()} with identical keys; tutorial code adapted with the same names for both.

Related errors


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