{"record":{"id":"3c448f3e5182e37d","repo":"cocoindex-io/cocoindex","slug":"serialize-by-pickle-on-cls-qualname-a-da","errorCode":null,"errorMessage":"@serialize_by_pickle on {cls.__qualname__} (a {'dataclass' if dataclasses.is_dataclass(cls) else 'NamedTuple' if issubclass(cls, tuple) else 'msgspec.Struct'}) has no effect when nested inside another msgspec-compatible type, because msgspec encodes these types natively and bypasses the pickle hook. Consider restructuring the type to be fully msgspec-compatible.","messagePattern":"@serialize_by_pickle on (.+?) \\(a (.+?)\\) has no effect when nested inside another msgspec-compatible type, because msgspec encodes these types natively and bypasses the pickle hook\\. Consider restructuring the type to be fully msgspec-compatible\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"python/cocoindex/_internal/serde.py","lineNumber":165,"sourceCode":"    _UNPICKLE_SAFE_GLOBALS[(module, qualname)] = obj\n\n\ndef _is_msgspec_native_type(cls: type) -> bool:\n    \"\"\"Check if a type is natively handled by msgspec (and thus bypasses enc_hook).\"\"\"\n    if dataclasses.is_dataclass(cls):\n        return True\n    if isinstance(cls, type) and issubclass(cls, tuple) and hasattr(cls, \"_fields\"):\n        # NamedTuple\n        return True\n    if isinstance(cls, type) and issubclass(cls, msgspec.Struct):\n        return True\n    return False\n\n\ndef serialize_by_pickle(cls: type) -> type:\n    \"\"\"Decorator: serialize this type with pickle. Auto-registers as unpickle-safe.\"\"\"\n    if _is_msgspec_native_type(cls):\n        warnings.warn(\n            f\"@serialize_by_pickle on {cls.__qualname__} (a \"\n            f\"{'dataclass' if dataclasses.is_dataclass(cls) else 'NamedTuple' if issubclass(cls, tuple) else 'msgspec.Struct'}\"\n            f\") has no effect when nested inside another msgspec-compatible type, \"\n            f\"because msgspec encodes these types natively and bypasses the pickle \"\n            f\"hook. Consider restructuring the type to be fully msgspec-compatible.\",\n            stacklevel=2,\n        )\n    _SERIALIZE_BY_PICKLE_TYPES.add(cls)\n    unpickle_safe(cls)\n    return cls\n\n\n# ---------------------------------------------------------------------------\n# Restricted unpickler\n# ---------------------------------------------------------------------------\n\n\nclass _RestrictedUnpickler(pickle.Unpickler):","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/serde.py#L147-L183","documentation":"Applying the @serialize_by_pickle decorator to a type that msgspec already encodes natively (dataclass, NamedTuple, or msgspec.Struct) has no effect when that type is nested inside another msgspec-compatible container: msgspec serializes it directly and never consults the pickle fallback hook. The decorator detects this at decoration time and warns so you don't falsely believe pickle is being used.","triggerScenarios":"Calling @serialize_by_pickle on (or applying it to) a class where _is_msgspec_native_type(cls) is True — i.e. a dataclass, a tuple subclass (NamedTuple), or msgspec.Struct — and later embedding it inside another msgspec-encoded type.","commonSituations":"Migrating a field type from a plain class to a dataclass/NamedTuple/Struct for typing reasons while keeping the old pickle decorator; the pickle hook silently stops firing for nested positions.","solutions":["Remove @serialize_by_pickle and make the type fully msgspec-compatible (ensure all its fields are msgspec-encodable) so native encoding works correctly.","If the type truly cannot be msgspec-encoded, change it so it is no longer a dataclass/NamedTuple/Struct (e.g. wrap the payload in a plain class or bytes) so the pickle hook actually applies.","Restructure the parent container so the field is not nested inside a msgspec-encoded type if pickle semantics are required."],"exampleFix":"// before\n@serialize_by_pickle\n@dataclass\nclass Embedding:\n    vec: Any  # not msgspec-encodable\n// after\n@dataclass\nclass Embedding:\n    vec: list[float]  # fully msgspec-compatible; drop the pickle decorator","handlingStrategy":"validation","validationCode":"import dataclasses\nfrom cocoindex._internal.serde import _is_msgspec_native_type\ndef pickle_hook_effective(cls: type) -> bool:\n    return not _is_msgspec_native_type(cls)  # warn-free only if True","typeGuard":"def is_msgspec_native(cls: type) -> bool:\n    return dataclasses.is_dataclass(cls) or issubclass(cls, tuple) or issubclass(cls, msgspec.Struct)","tryCatchPattern":"import warnings\nwith warnings.catch_warnings():\n    warnings.simplefilter(\"error\", UserWarning)\n    try:\n        decorated = serialize_by_pickle(MyType)\n    except UserWarning:\n        ...  # type is msgspec-native; make it msgspec-compatible instead","preventionTips":["Don't stack @serialize_by_pickle on dataclasses, NamedTuples, or msgspec.Structs.","Make shared field types fully msgspec-encodable instead of relying on pickle fallback.","Add a unit test asserting the decorator warns for msgspec-native types you intentionally keep."],"tags":["serialization","msgspec","pickle","decorator","python"],"backgroundTag":"deprecated-api-usage","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}