{"record":{"id":"bbc98099a2ae4ab5","repo":"cocoindex-io/cocoindex","slug":"type-obj-name-cannot-be-used-as-a-memoizati","errorCode":null,"errorMessage":"{type(obj).__name__} cannot be used as a memoization key. This type maintains internal state that is incompatible with memoization.","messagePattern":"(.+?) cannot be used as a memoization key\\. This type maintains internal state that is incompatible with memoization\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/memo_fingerprint.py","lineNumber":224,"sourceCode":"\n    _memo_fns[typ] = _MemoFns(key_fn, state_fn)\n\n\ndef register_not_memo_keyable(typ: type) -> None:\n    \"\"\"Register a type as not memo-keyable.\n\n    Use this for third-party types that maintain internal state incompatible\n    with memoization, but which you cannot modify to inherit from `NotMemoKeyable`.\n\n    Example:\n        import cocoindex as coco\n        from some_library import StatefulGenerator\n\n        coco.register_not_memo_keyable(StatefulGenerator)\n    \"\"\"\n\n    def _raise_not_memo_keyable(obj: object) -> typing.NoReturn:\n        raise TypeError(\n            f\"{type(obj).__name__} cannot be used as a memoization key. \"\n            \"This type maintains internal state that is incompatible with memoization.\"\n        )\n\n    _memo_fns[typ] = _MemoFns(_raise_not_memo_keyable)\n\n\ndef unregister_memo_key_function(typ: type) -> None:\n    \"\"\"Remove a previously registered memo key function (best-effort).\"\"\"\n\n    _memo_fns.pop(typ, None)\n\n\ndef _stable_sort_key(v: Fingerprintable) -> tuple[typing.Any, ...]:\n    \"\"\"Return a totally-ordered key for canonical values.\n\n    This is used to deterministically sort dict/set canonical encodings without\n    relying on Python comparing heterogeneous values directly.","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/memo_fingerprint.py#L206-L242","documentation":"CocoIndex memoization fingerprints arguments to build a memoization key. Objects of types explicitly registered via coco.register_not_memo_keyable() are rejected because they carry internal state (e.g. open files, RNGs, generators) that would make an identical key even though the object differs. A TypeError is raised at call time when such an object is passed to a memoized function.","triggerScenarios":"Passing an instance of a type registered with coco.register_not_memo_keyable() (like StatefulGenerator in the docstring example) as an argument to a function decorated with @coco.fn(memo=True).","commonSituations":"Developers wrapping stateful objects — generators, file handles, streams, connection objects — as arguments to memoized functions, often after the library author or their own code deliberately excluded that type from memoization keys.","solutions":["Remove the stateful object from the memoized function's arguments and pass only immutable, fingerprintable values (str, int, bytes, dataclasses of such).","Pass a stable key derived from the object (e.g. its path, seed, or id) instead of the object itself.","If the type is actually safe to key, remove it from the register_not_memo_keyable() registration in your own code.","Disable memoization (memo=False) for functions that must accept stateful objects."],"exampleFix":"// before\n@coco.fn(memo=True)\nasync def run(gen: StatefulGenerator) -> int:\n    return gen.next()\nrun(StatefulGenerator())\n\n// after\n@coco.fn(memo=True)\nasync def run(seed: int) -> int:\n    gen = StatefulGenerator(seed)\n    return gen.next()\nrun(42)","handlingStrategy":"type-guard","validationCode":"if isinstance(arg, StatefulGenerator):\n    raise ValueError(\"stateful object cannot be a memoized argument\")","typeGuard":"def is_memo_keyable(obj: object) -> bool:\n    return hasattr(obj, \"__coco_memo_key__\") or isinstance(obj, (str, int, float, bytes, bool, type(None), tuple))","tryCatchPattern":"try:\n    result = await memoized_fn(arg)\nexcept TypeError as e:\n    if \"memoization key\" in str(e):\n        result = await run_without_memo(arg)\n    else:\n        raise","preventionTips":["Only pass immutable primitives or dataclasses of primitives to memoized functions","Never pass generators, streams, or connection objects as memoized arguments","Inspect register_not_memo_keyable registrations before choosing argument types"],"tags":["python","memoization","type-error"],"backgroundTag":"invalid-argument-value","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"}