run-llama/llama_index · error · ValueError

Objs is not pickleable

Error message

Objs is not pickleable

What it means

SimpleObjectNodeMapping.persist pickles the whole mapping (self, including every object) to <persist_dir>/object_node_mapping.pkl. If any object is not pickleable, pickle.PickleError is re-raised as ValueError('Objs is not pickleable'). Common unpickleable values include lambdas/closures, open file/socket/DB-engine handles, and objects holding thread locks.

Source

Thrown at llama-index-core/llama_index/core/objects/base_node_mapping.py:207

    def persist(
        self,
        persist_dir: str = DEFAULT_PERSIST_DIR,
        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,
    ) -> None:
        """
        Persist object node mapping.

        NOTE: This may fail depending on whether the object types are
        pickle-able.
        """
        if not os.path.exists(persist_dir):
            os.makedirs(persist_dir)
        obj_node_mapping_path = concat_dirs(persist_dir, obj_node_mapping_fname)
        try:
            with open(obj_node_mapping_path, "wb") as f:
                pickle.dump(self, f)
        except pickle.PickleError as err:
            raise ValueError("Objs is not pickleable") from err

    @classmethod
    def from_persist_dir(
        cls,
        persist_dir: str = DEFAULT_PERSIST_DIR,
        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,
    ) -> "SimpleObjectNodeMapping":
        obj_node_mapping_path = concat_dirs(persist_dir, obj_node_mapping_fname)
        try:
            with open(obj_node_mapping_path, "rb") as f:
                simple_object_node_mapping = _RestrictedUnpickler(f).load()
        except pickle.PickleError as err:
            raise ValueError("Objs cannot be loaded.") from err
        return simple_object_node_mapping

View on GitHub (pinned to afd0fef371)

Solutions

  1. Remove non-pickleable attributes from stored objects (e.g. detach the engine/client, implement __getstate__/__setstate__ to drop it)
  2. Replace lambdas with module-level functions or functools.partial over pickleable args
  3. Use a custom BaseObjectNodeMapping subclass that persists only what it needs (e.g. store tool names/configs) instead of the whole object
  4. Skip mapping persistence and rebuild the ObjectIndex from objects at load time, passing object_node_mapping to ObjectIndex.from_persist_dir

Example fix

# before
@dataclass
class Doc:
    name: str
    session: requests.Session  # not pickleable -> ValueError: Objs is not pickleable

# after
@dataclass
class Doc:
    name: str
    session: Optional[requests.Session] = None

    def __getstate__(self):
        state = self.__dict__.copy()
        state["session"] = None  # drop live handle before pickling
        return state
Defensive patterns

Strategy: validation

Validate before calling

import pickle
test = pickle.dumps(mapping)  # dry-run picklability before persisting to disk

Type guard

def is_pickleable(obj) -> bool:
    import pickle
    try:
        pickle.dumps(obj)
        return True
    except (pickle.PickleError, TypeError, AttributeError):
        return False

Try / catch

try:
    mapping.persist(persist_dir)
except ValueError as e:
    if "not pickleable" in str(e):
        # fall back: persist index only, rebuild mapping from objects at load time
        obj_index.index.storage_context.persist(persist_dir=persist_dir)
    else:
        raise

Prevention

When it happens

Trigger: Calling obj_index.persist(persist_dir) (or mapping.persist) on a SimpleObjectNodeMapping whose objects list contains lambdas, SQLAlchemy engines/sessions, LangChain tools wrapping non-pickleable callables, or objects with __slots__ misconfigured / __reduce__ raising.

Common situations: ObjectIndex built over QueryEngineTool objects that capture a live LLM/embeddings client; objects holding a requests.Session or cuda tensors; persisting in notebooks where objects are closures; switching from plain dataclasses to ones holding a client object.

Related errors


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