{"record":{"id":"51da459cd3243c8c","repo":"cocoindex-io/cocoindex","slug":"type-self-name-cannot-be-used-as-a-memoizat","errorCode":null,"errorMessage":"{type(self).__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":190,"sourceCode":"            for name in field_names\n        ),\n    )\n\n\nclass NotMemoKeyable:\n    \"\"\"\n    Base class for objects that must not be used as memoization keys.\n\n    Inherit from this class when an object maintains internal state that would\n    make memoization semantically incorrect (e.g., generators that track call counts).\n\n    Attempting to use a `NotMemoKeyable` instance as a memo key will raise TypeError.\n    \"\"\"\n\n    __slots__ = ()\n\n    def __coco_memo_key__(self) -> typing.NoReturn:\n        raise TypeError(\n            f\"{type(self).__name__} cannot be used as a memoization key. \"\n            \"This type maintains internal state that is incompatible with memoization.\"\n        )\n\n\ndef register_memo_key_function(\n    typ: type, key_fn: _KeyFn, *, state_fn: _StateFn | None = None\n) -> None:\n    \"\"\"Register a memo key function for a type.\n\n    Resolution is MRO-aware: the most specific registered base type wins.\n\n    If *state_fn* is provided it is stored separately and used for memo state\n    validation (see ``_canonicalize``).\n    \"\"\"\n\n    _memo_fns[typ] = _MemoFns(key_fn, state_fn)\n","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/memo_fingerprint.py#L172-L208","documentation":"Types marked as NotMemoKeyable implement __coco_memo_key__ to deliberately raise TypeError when used as a memoization key, because they hold internal state (e.g. connection pools, handles) whose identity is not stable across runs and would produce incorrect memo hits.","triggerScenarios":"Passing an instance of a stateful type (one registered/defined as NotMemoKeyable) as an argument to a @coco.fn(memo=True) function, so fingerprinting invokes __coco_memo_key__.","commonSituations":"Accidentally passing a database pool, client handle, or environment object as a function argument to a memoized fn instead of obtaining it via coco.use_context; wrapping functions that take live resources.","solutions":["Remove the stateful object from the memoized function's arguments; fetch it inside via coco.use_context(key).","Turn off memoization (memo=False / default) if the argument is genuinely needed.","Pass a stable key/identifier (str) instead of the live object itself."],"exampleFix":"// before\n@coco.fn(memo=True)\nasync def query(pool: Pool, sql: str): ...\n// after\n@coco.fn(memo=True)\nasync def query(sql: str) -> ...:\n    pool = coco.use_context(PG_DB)","handlingStrategy":"type-guard","validationCode":"def memo_safe(value) -> bool:\n    return not hasattr(type(value), '__coco_memo_key__') or type(value).__coco_memo_key__ is not typing.NoReturn","typeGuard":"def is_memo_keyable(x) -> bool:\n    try:\n        x.__coco_memo_key__ if hasattr(x, '__coco_memo_key__') else True\n        return True\n    except TypeError:\n        return False","tryCatchPattern":"try:\n    result = await memoized_fn(resource, arg)\nexcept TypeError as e:\n    if \"memoization key\" in str(e):\n        result = await memoized_fn(arg)  # fetch resource via use_context inside","preventionTips":["Never pass pools, clients, or other stateful handles as memoized fn arguments.","Obtain live resources via coco.use_context inside the function body.","Disable memo=True for functions that must take stateful arguments."],"tags":["python","memoization","type-restriction"],"backgroundTag":"type-mismatch","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"}