{"record":{"id":"078a4f6c3564cb01","repo":"cocoindex-io/cocoindex","slug":"livecomponentoperator-is-no-longer-active-operato","errorCode":null,"errorMessage":"LiveComponentOperator is no longer active. Operator methods are only valid inside the body of process_live.","messagePattern":"LiveComponentOperator is no longer active\\. Operator methods are only valid inside the body of process_live\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/live_component.py","lineNumber":268,"sourceCode":"        self._env = env\n        self._path = path\n\n    def _detach(self) -> None:\n        \"\"\"Release the Rust controller; subsequent operator calls raise.\n\n        Called by ``_process_live_wrapper`` in a ``finally`` block once\n        ``process_live`` returns. After detach, the operator is usable only\n        for inspecting its own metadata (``_env``, ``_path``); the\n        controller-backed methods (:meth:`update_full`, :meth:`update`,\n        :meth:`delete`, :meth:`mark_ready`) raise :class:`RuntimeError`.\n        \"\"\"\n        self._controller = None\n\n    def _require_controller(self) -> core.LiveComponentController:\n        \"\"\"Return the controller or raise if already detached.\"\"\"\n        ctrl = self._controller\n        if ctrl is None:\n            raise RuntimeError(\n                \"LiveComponentOperator is no longer active. Operator \"\n                \"methods are only valid inside the body of process_live.\"\n            )\n        return ctrl\n\n    def _resolve_exception_handler(self) -> Callable[[str], Awaitable[None]]:\n        \"\"\"Build a resolver for the parent's exception handler chain.\n\n        Delegates to :meth:`ComponentContext.resolve_exception_handler`\n        — the same path used by ``coco.mount`` / ``coco.mount_each`` —\n        so component-failure logs go through one canonical Python\n        fallback. Always non-None. Used both by :meth:`update_full`\n        (passes to Rust as ``on_error``) and :meth:`report_exception`\n        (invokes directly with a stringified exception).\n        \"\"\"\n        return get_context_from_ctx().resolve_exception_handler(\n            stable_path=self._path.to_string(),\n            processor_name=type(self._instance).__name__,","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/live_component.py#L250-L286","documentation":"A LiveComponentOperator is detached (its _controller set to None) once the process_live body finishes. Any operator method afterward — update, update_full, delete, mark_ready, state reads/writes — raises RuntimeError because the operator is only valid within the process_live body scope.","triggerScenarios":"Storing the operator passed to process_live in a variable/closure and calling op.update()/op.delete()/etc. after the process_live body returns; using the operator from another task or a later callback.","commonSituations":"Keeping a reference to the operator on self or in a global for later use; spawning a background task inside process_live that outlives the body and touches the operator.","solutions":["Perform all operator calls inside the process_live body; await them before returning.","Do not store the operator beyond the body's lifetime; capture needed data instead.","Re-enter process_live to get a fresh operator for new updates."],"exampleFix":"// before\nasync def body(op):\n    tasks.append(asyncio.create_task(do_update(op)))\n// after\nasync def body(op):\n    await do_update(op)  # finish within the body","handlingStrategy":"try-catch","validationCode":"if operator._controller is None:\n    raise RuntimeError(\"operator detached; re-enter process_live\")","typeGuard":"def is_active(op) -> bool: return getattr(op, '_controller', None) is not None","tryCatchPattern":"try:\n    await op.update(key, fn, args)\nexcept RuntimeError as e:\n    if \"no longer active\" in str(e):\n        schedule_in_next_process_live(fn, args)","preventionTips":["Never store the process_live operator beyond the body; await all calls inside it.","Avoid background tasks that outlive the process_live body and touch the operator."],"tags":["python","live-component","lifecycle","use-after-free"],"backgroundTag":"invalid-state-transition","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"}