run-llama/llama_index · error · ValueError

ref_doc_id of node cannot be None when building a document s

Error message

ref_doc_id of node cannot be None when building a document summary index

What it means

DocumentSummaryIndex groups nodes by their ref_doc_id (the source document id) to build per-document summaries. During _add_nodes_to_index, any node whose ref_doc_id is None cannot be attributed to a document, so the build fails immediately with ValueError. Nodes parsed through the standard node-parser pipeline always carry ref_doc_id; hand-constructed nodes often don't.

Source

Thrown at llama-index-core/llama_index/core/indices/document_summary/base.py:175

            doc_id (str): A document id.

        """
        if doc_id not in self._index_struct.doc_id_to_summary_id:
            raise ValueError(f"doc_id {doc_id} not in index")
        summary_id = self._index_struct.doc_id_to_summary_id[doc_id]
        return self.docstore.get_node(summary_id).get_content()

    def _add_nodes_to_index(
        self,
        index_struct: IndexDocumentSummary,
        nodes: Sequence[BaseNode],
        show_progress: bool = False,
    ) -> None:
        """Add nodes to index."""
        doc_id_to_nodes = defaultdict(list)
        for node in nodes:
            if node.ref_doc_id is None:
                raise ValueError(
                    "ref_doc_id of node cannot be None when building a document "
                    "summary index"
                )
            doc_id_to_nodes[node.ref_doc_id].append(node)

        summary_node_dict = {}
        items = doc_id_to_nodes.items()
        iterable_with_progress = get_tqdm_iterable(
            items, show_progress, "Summarizing documents"
        )

        for doc_id, nodes in iterable_with_progress:
            print(f"current doc id: {doc_id}")
            nodes_with_scores = [NodeWithScore(node=n) for n in nodes]
            # get the summary for each doc_id
            summary_response = self._response_synthesizer.synthesize(
                query=self._summary_query,
                nodes=nodes_with_scores,

View on GitHub (pinned to afd0fef371)

Solutions

  1. Build nodes through a node parser: SentenceSplitter().get_nodes_from_documents(docs) — these set ref_doc_id automatically.
  2. If constructing nodes manually, set the source relationship: node.relationships[NodeRelationship.SOURCE] = RelatedNodeInfo(source_id=doc_id) or pass source_node to TextNode.
  3. Filter/reject nodes with ref_doc_id None before insert: nodes = [n for n in nodes if n.ref_doc_id is not None].

Example fix

# before
nodes = [TextNode(text=t) for t in chunks]  # no ref_doc_id -> ValueError
index = DocumentSummaryIndex(nodes=nodes)

# after
from llama_index.core.schema import TextNode, RelatedNodeInfo, NodeRelationship
nodes = [
    TextNode(
        text=t,
        relationships={NodeRelationship.SOURCE: RelatedNodeInfo(source_id=f"doc-{i}")},
    )
    for i, t in enumerate(chunks)
]
index = DocumentSummaryIndex(nodes=nodes)
Defensive patterns

Strategy: validation

Validate before calling

orphaned = [n.id_ for n in nodes if n.ref_doc_id is None]
if orphaned:
    raise ValueError(f"Nodes missing SOURCE relationship: {orphaned}")

Type guard

def all_nodes_have_source(nodes) -> bool:
    return all(n.ref_doc_id is not None for n in nodes)

Prevention

When it happens

Trigger: Building DocumentSummaryIndex.from_documents with a custom transformation that returns TextNode objects built directly (no relationships); inserting nodes via index.insert_nodes where nodes lack a SOURCE relationship; loading nodes from external storage without restoring relationships.

Common situations: Custom node pipelines that create TextNode(text=...) manually; caching layers that serialize nodes to dicts and lose the Source relationship; converting other frameworks' chunks into llama-index nodes.

Related errors


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