{"record":{"id":"2e63cd380588e051","repo":"cocoindex-io/cocoindex","slug":"coco-use-state-cannot-be-called-inside-a-memoize","errorCode":null,"errorMessage":"coco.use_state() cannot be called inside a memoized function","messagePattern":"coco\\.use_state\\(\\) cannot be called inside a memoized function","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/api.py","lineNumber":867,"sourceCode":"\n        # Typed — handle.value is Cursor, with full type inference\n        @dataclass\n        class Cursor:\n            pos: int\n            tag: str\n\n        cur = coco.use_state(\"cursor\", type_hint=Cursor, initial_value=Cursor(0, \"init\"))\n        cur.value.pos += 1\n        cur.value = Cursor(cur.value.pos, \"next\")\n    \"\"\"\n    ctx = get_context_from_ctx()\n    if ctx._core_path != ctx._core_processor_ctx.stable_path:\n        raise RuntimeError(\n            \"coco.use_state() cannot be called inside a `with coco.component_subpath()` block\"\n        )\n\n    if ctx._in_memo_fn:\n        raise RuntimeError(\n            \"coco.use_state() cannot be called inside a memoized function\"\n        )\n    try:\n        # initial_value passed unserialized; engine core drops it if a value is\n        # already stored on the previous run for this key.\n        stored = ctx._core_processor_ctx.use_state(key, initial_value)\n    except ValueError as e:\n        # Rust client errors surface as ValueError; normalize to RuntimeError so\n        # all use_state usage errors have a consistent type for callers.\n        raise RuntimeError(str(e)) from None\n    if type_hint is not None:\n        deserializer = get_deserialize_fn(\n            type_hint,  # type: ignore[arg-type]  # type objects are hashable at runtime\n            source_label=f\"use_state key {key!r}\",\n        )\n    else:\n        deserializer = _DESERIALIZE_ANY\n    return StateHandle(key, stored, deserializer, ctx._core_processor_ctx)","sourceCodeStart":849,"sourceCodeEnd":885,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/api.py#L849-L885","documentation":"Memoized functions (@coco.fn(memo=True)) must be pure and replayable: their result depends only on inputs and code. use_state() introduces persistent component state, which breaks memoization semantics, so calling it inside a memoized function is disallowed.","triggerScenarios":"Calling coco.use_state(...) from within a function decorated with @coco.fn(memo=True) (detected via ctx._in_memo_fn).","commonSituations":"Adding state to a memoized helper when converting it into a component; copy-pasting component-body code (with use_state) into a memoized function; misunderstanding memo=True as 'cache the state too'.","solutions":["Remove memo=True from the function so it can hold component state","Move the use_state() call out of the memoized function into the owning component body","Pass the state value into the memoized function as a plain argument instead"],"exampleFix":"// before\n@coco.fn(memo=True)\nasync def step(ctx):\n    cur = coco.use_state(\"cursor\", initial_value=0)\n// after\nasync def component_main(ctx):\n    cur = coco.use_state(\"cursor\", initial_value=0)\n    await step(ctx, cur.value)","handlingStrategy":"validation","validationCode":"# never call coco.use_state inside @coco.fn(memo=True) functions\n# pass state values in as arguments instead:\nasync def step(ctx, cursor): ...","typeGuard":null,"tryCatchPattern":"try:\n    cur = coco.use_state(\"k\", initial_value=0)\nexcept RuntimeError as e:\n    if \"memoized function\" in str(e):\n        raise  # move state to the owning component and pass it in","preventionTips":["Keep memo=True functions pure: no state, no I/O side effects on component state","Hoist use_state to the component body and pass values into memoized helpers","Review any function converted to memo=True for use_state usage"],"tags":["python","state","memoization"],"backgroundTag":"unsupported-operation","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"}