run-llama/llama_index · error · ValueError

Source object must be a single RelatedNodeInfo object

Error message

Source object must be a single RelatedNodeInfo object

What it means

TextNode.source_node reads relationships[NodeRelationship.SOURCE] and requires it to be a single RelatedNodeInfo, not a list. The SOURCE relationship is singular by schema contract (each node has at most one source document), so a list value is treated as malformed node data and raises ValueError.

Source

Thrown at llama-index-core/llama_index/core/schema.py:403

    @node_id.setter
    def node_id(self, value: str) -> None:
        self.id_ = value

    @property
    def source_node(self) -> Optional[RelatedNodeInfo]:
        """
        Source object node.

        Extracted from the relationships field.

        """
        if NodeRelationship.SOURCE not in self.relationships:
            return None

        relation = self.relationships[NodeRelationship.SOURCE]
        if isinstance(relation, list):
            raise ValueError("Source object must be a single RelatedNodeInfo object")
        return relation

    @property
    def prev_node(self) -> Optional[RelatedNodeInfo]:
        """Prev node."""
        if NodeRelationship.PREVIOUS not in self.relationships:
            return None

        relation = self.relationships[NodeRelationship.PREVIOUS]
        if not isinstance(relation, RelatedNodeInfo):
            raise ValueError("Previous object must be a single RelatedNodeInfo object")
        return relation

    @property
    def next_node(self) -> Optional[RelatedNodeInfo]:
        """Next node."""
        if NodeRelationship.NEXT not in self.relationships:
            return None

View on GitHub (pinned to afd0fef371)

Solutions

  1. Set the SOURCE relationship to a single RelatedNodeInfo object, not a list
  2. If you loaded the node from JSON, fix the serialized form: "1": {...} not "1": [{...}]
  3. Write a normalization pass over relationships before ingest that unwraps one-element lists for SOURCE

Example fix

# before
node.relationships[NodeRelationship.SOURCE] = [RelatedNodeInfo(node_id=doc_id)]

# after
node.relationships[NodeRelationship.SOURCE] = RelatedNodeInfo(node_id=doc_id)
Defensive patterns

Strategy: validation

Validate before calling

rel = node.relationships.get(NodeRelationship.SOURCE)
if isinstance(rel, list):
    raise TypeError("SOURCE must be singular; fix ingestion")  # or unwrap: rel[0] if len(rel)==1

Type guard

from llama_index.core.schema import RelatedNodeInfo
from llama_index.core.schema import NodeRelationship
def has_valid_source(node) -> bool:
    rel = node.relationships.get(NodeRelationship.SOURCE)
    return rel is None or isinstance(rel, RelatedNodeInfo)

Prevention

When it happens

Trigger: Manually setting node.relationships[NodeRelationship.SOURCE] = [RelatedNodeInfo(...), ...], or loading/deserializing nodes whose SOURCE relation was serialized as a list; then calling .source_node or legacy .ref_doc_id.

Common situations: Custom node builders or ETL code that reuses the CHILD pattern (which is a list) for SOURCE; nodes round-tripped through older versions or hand-written JSON where SOURCE ended up as an array.

Related errors


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