run-llama/llama_index · error · ValueError

IndexNode obj is not serializable: {obj}

Error message

IndexNode obj is not serializable: {obj}

What it means

IndexNode can wrap an arbitrary Python object in its obj field (used by recursive retrieval over agents, query engines, retrievers). When serializing, _serialize_obj tries doc_to_json for BaseNode, model_dump for pydantic BaseModel, and json.dumps for everything else; if all fail it raises ValueError('IndexNode obj is not serializable').

Source

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

    """

    index_id: str
    obj: Any = None

    def _serialize_obj(self) -> Any:
        from llama_index.core.storage.docstore.utils import doc_to_json

        try:
            if self.obj is None:
                return None
            elif isinstance(self.obj, BaseNode):
                return doc_to_json(self.obj)
            elif isinstance(self.obj, BaseModel):
                return self.obj.model_dump()
            else:
                return json.dumps(self.obj)
        except Exception:
            raise ValueError("IndexNode obj is not serializable: " + str(self.obj))

    @model_serializer(mode="wrap")
    def custom_model_dump(
        self, handler: SerializerFunctionWrapHandler, info: SerializationInfo
    ) -> Dict[str, Any]:
        data = super().custom_model_dump(handler, info)
        data["obj"] = self._serialize_obj()
        return data

    def dict(self, **kwargs: Any) -> Dict[str, Any]:
        data = super().dict(**kwargs)
        data["obj"] = self._serialize_obj()
        return data

    @classmethod
    def from_text_node(
        cls,
        node: TextNode,

View on GitHub (pinned to afd0fef371)

Solutions

  1. Wrap the object in a pydantic BaseModel (or make it a dataclass supporting json.dumps) so one of the three serialization branches succeeds
  2. Store a serializable reference (id/string/config) in obj and keep the live object in an object_map instead
  3. Set obj=None for nodes that only need to exist as index structure

Example fix

# before
index_node = IndexNode(text="router", obj=my_retriever)  # not serializable

# after
from pydantic import BaseModel
class RetrieverRef(BaseModel):
    retriever_name: str
index_node = IndexNode(text="router", obj=RetrieverRef(retriever_name="base"))
# live retriever stays in the object_map / recursive_retriever mapping
Defensive patterns

Strategy: validation

Validate before calling

import json
from pydantic import BaseModel
def obj_is_serializable(obj) -> bool:
    if obj is None or isinstance(obj, (BaseNode, BaseModel)):
        return True
    try:
        json.dumps(obj)
        return True
    except (TypeError, ValueError):
        return False

Type guard

def index_node_safe_to_persist(n: "IndexNode") -> bool:
    return obj_is_serializable(n.obj)

Prevention

When it happens

Trigger: Creating IndexNode(obj=<non-serializable object>) — e.g. a raw retriever/query engine instance, a lambda, or an object holding open connections — and then persisting it (docstore.save, json()/model_dump, storage context persist).

Common situations: Recursive/agent retrieval where obj holds live objects; calling index.storage_context.persist() with object-bearing IndexNodes in the docstore; deep-copy or caching layers that serialize the node.

Related errors


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