{"record":{"id":"a54c11610987fd4d","repo":"run-llama/llama_index","slug":"objs-is-not-pickleable","errorCode":null,"errorMessage":"Objs is not pickleable","messagePattern":"Objs is not pickleable","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/objects/base_node_mapping.py","lineNumber":207,"sourceCode":"    def persist(\n        self,\n        persist_dir: str = DEFAULT_PERSIST_DIR,\n        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,\n    ) -> None:\n        \"\"\"\n        Persist object node mapping.\n\n        NOTE: This may fail depending on whether the object types are\n        pickle-able.\n        \"\"\"\n        if not os.path.exists(persist_dir):\n            os.makedirs(persist_dir)\n        obj_node_mapping_path = concat_dirs(persist_dir, obj_node_mapping_fname)\n        try:\n            with open(obj_node_mapping_path, \"wb\") as f:\n                pickle.dump(self, f)\n        except pickle.PickleError as err:\n            raise ValueError(\"Objs is not pickleable\") from err\n\n    @classmethod\n    def from_persist_dir(\n        cls,\n        persist_dir: str = DEFAULT_PERSIST_DIR,\n        obj_node_mapping_fname: str = DEFAULT_PERSIST_FNAME,\n    ) -> \"SimpleObjectNodeMapping\":\n        obj_node_mapping_path = concat_dirs(persist_dir, obj_node_mapping_fname)\n        try:\n            with open(obj_node_mapping_path, \"rb\") as f:\n                simple_object_node_mapping = _RestrictedUnpickler(f).load()\n        except pickle.PickleError as err:\n            raise ValueError(\"Objs cannot be loaded.\") from err\n        return simple_object_node_mapping\n","sourceCodeStart":189,"sourceCodeEnd":222,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/objects/base_node_mapping.py#L189-L222","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove non-pickleable attributes from stored objects (e.g. detach the engine/client, implement __getstate__/__setstate__ to drop it)","Replace lambdas with module-level functions or functools.partial over pickleable args","Use a custom BaseObjectNodeMapping subclass that persists only what it needs (e.g. store tool names/configs) instead of the whole object","Skip mapping persistence and rebuild the ObjectIndex from objects at load time, passing object_node_mapping to ObjectIndex.from_persist_dir"],"exampleFix":"# before\n@dataclass\nclass Doc:\n    name: str\n    session: requests.Session  # not pickleable -> ValueError: Objs is not pickleable\n\n# after\n@dataclass\nclass Doc:\n    name: str\n    session: Optional[requests.Session] = None\n\n    def __getstate__(self):\n        state = self.__dict__.copy()\n        state[\"session\"] = None  # drop live handle before pickling\n        return state","handlingStrategy":"validation","validationCode":"import pickle\ntest = pickle.dumps(mapping)  # dry-run picklability before persisting to disk","typeGuard":"def is_pickleable(obj) -> bool:\n    import pickle\n    try:\n        pickle.dumps(obj)\n        return True\n    except (pickle.PickleError, TypeError, AttributeError):\n        return False","tryCatchPattern":"try:\n    mapping.persist(persist_dir)\nexcept ValueError as e:\n    if \"not pickleable\" in str(e):\n        # fall back: persist index only, rebuild mapping from objects at load time\n        obj_index.index.storage_context.persist(persist_dir=persist_dir)\n    else:\n        raise","preventionTips":["Keep persisted objects as plain data (dataclasses/dicts of primitives)","Exclude live handles via __getstate__ (return a copy of __dict__ minus the handle)","Never store lambdas/closures in objects meant to be persisted"],"tags":["persistence","pickle","object-index","serialization"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}