cocoindex-io/cocoindex · error · TypeError
{type(self).__name__} cannot be used as a memoization key. T
Error message
{type(self).__name__} cannot be used as a memoization key. This type maintains internal state that is incompatible with memoization. What it means
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.
Source
Thrown at python/cocoindex/_internal/memo_fingerprint.py:190
for name in field_names
),
)
class NotMemoKeyable:
"""
Base class for objects that must not be used as memoization keys.
Inherit from this class when an object maintains internal state that would
make memoization semantically incorrect (e.g., generators that track call counts).
Attempting to use a `NotMemoKeyable` instance as a memo key will raise TypeError.
"""
__slots__ = ()
def __coco_memo_key__(self) -> typing.NoReturn:
raise TypeError(
f"{type(self).__name__} cannot be used as a memoization key. "
"This type maintains internal state that is incompatible with memoization."
)
def register_memo_key_function(
typ: type, key_fn: _KeyFn, *, state_fn: _StateFn | None = None
) -> None:
"""Register a memo key function for a type.
Resolution is MRO-aware: the most specific registered base type wins.
If *state_fn* is provided it is stored separately and used for memo state
validation (see ``_canonicalize``).
"""
_memo_fns[typ] = _MemoFns(key_fn, state_fn)
View on GitHub (pinned to e84aa99b32)
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.
Example fix
// before
@coco.fn(memo=True)
async def query(pool: Pool, sql: str): ...
// after
@coco.fn(memo=True)
async def query(sql: str) -> ...:
pool = coco.use_context(PG_DB) Defensive patterns
Strategy: type-guard
Validate before calling
def memo_safe(value) -> bool:
return not hasattr(type(value), '__coco_memo_key__') or type(value).__coco_memo_key__ is not typing.NoReturn Type guard
def is_memo_keyable(x) -> bool:
try:
x.__coco_memo_key__ if hasattr(x, '__coco_memo_key__') else True
return True
except TypeError:
return False Try / catch
try:
result = await memoized_fn(resource, arg)
except TypeError as e:
if "memoization key" in str(e):
result = await memoized_fn(arg) # fetch resource via use_context inside Prevention
- 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.
When it happens
Trigger: 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__.
Common situations: 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.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- coco.use_state() cannot be called inside a memoized function
- Memo state function returned an awaitable from a sync contex
- {type(obj).__name__} cannot be used as a memoization key. Th
- Unsupported type for memoization key: {type(obj)!r}. Provide
- use_mount() requires a ComponentSubpath when the function ha
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/51da459cd3243c8c.
Report an issue: GitHub.