{"record":{"id":"d6bc24db7dd7fc90","repo":"run-llama/llama_index","slug":"source-object-must-be-a-single-relatednodeinfo-obj","errorCode":null,"errorMessage":"Source object must be a single RelatedNodeInfo object","messagePattern":"Source object must be a single RelatedNodeInfo object","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/schema.py","lineNumber":403,"sourceCode":"\n    @node_id.setter\n    def node_id(self, value: str) -> None:\n        self.id_ = value\n\n    @property\n    def source_node(self) -> Optional[RelatedNodeInfo]:\n        \"\"\"\n        Source object node.\n\n        Extracted from the relationships field.\n\n        \"\"\"\n        if NodeRelationship.SOURCE not in self.relationships:\n            return None\n\n        relation = self.relationships[NodeRelationship.SOURCE]\n        if isinstance(relation, list):\n            raise ValueError(\"Source object must be a single RelatedNodeInfo object\")\n        return relation\n\n    @property\n    def prev_node(self) -> Optional[RelatedNodeInfo]:\n        \"\"\"Prev node.\"\"\"\n        if NodeRelationship.PREVIOUS not in self.relationships:\n            return None\n\n        relation = self.relationships[NodeRelationship.PREVIOUS]\n        if not isinstance(relation, RelatedNodeInfo):\n            raise ValueError(\"Previous object must be a single RelatedNodeInfo object\")\n        return relation\n\n    @property\n    def next_node(self) -> Optional[RelatedNodeInfo]:\n        \"\"\"Next node.\"\"\"\n        if NodeRelationship.NEXT not in self.relationships:\n            return None","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/schema.py#L385-L421","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set the SOURCE relationship to a single RelatedNodeInfo object, not a list","If you loaded the node from JSON, fix the serialized form: \"1\": {...} not \"1\": [{...}]","Write a normalization pass over relationships before ingest that unwraps one-element lists for SOURCE"],"exampleFix":"# before\nnode.relationships[NodeRelationship.SOURCE] = [RelatedNodeInfo(node_id=doc_id)]\n\n# after\nnode.relationships[NodeRelationship.SOURCE] = RelatedNodeInfo(node_id=doc_id)","handlingStrategy":"validation","validationCode":"rel = node.relationships.get(NodeRelationship.SOURCE)\nif isinstance(rel, list):\n    raise TypeError(\"SOURCE must be singular; fix ingestion\")  # or unwrap: rel[0] if len(rel)==1","typeGuard":"from llama_index.core.schema import RelatedNodeInfo\nfrom llama_index.core.schema import NodeRelationship\ndef has_valid_source(node) -> bool:\n    rel = node.relationships.get(NodeRelationship.SOURCE)\n    return rel is None or isinstance(rel, RelatedNodeInfo)","tryCatchPattern":null,"preventionTips":["Never reuse the CHILD list pattern for SOURCE","Normalize relationship shapes in one place during ingestion","Add schema assertions in tests for custom node builders"],"tags":["schema","nodes","relationships","data-validation"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}