cocoindex-io/cocoindex · error · RuntimeError

{api_name}() is not allowed inside process_live; use operato

Error message

{api_name}() is not allowed inside process_live; use operator.update / operator.delete instead. (See specs/live_component/requirement.md for the rationale.)

What it means

Inside a process_live body, structural mount APIs (coco.mount, coco.mount_each, coco.use_mount) are forbidden: live components must express changes through operator.update / operator.delete. check_not_in_process_live is called at the entry of those APIs and raises RuntimeError when the process_live context variable is set.

Source

Thrown at python/cocoindex/_internal/live_component.py:93

#   inheritance chain holds. Future-proofing: integration tests verify both:
#     (a) `coco.mount(...)` directly inside `process_live` raises
#     (b) `coco.mount(...)` inside `process()` of a live component does NOT raise
#   If (a) silently stops raising, the wrapper isn't installed; if (b)
#   starts raising, the symmetric reset isn't taking effect — both are
#   load-bearing for the live-component installer machinery.
_in_process_live: ContextVar[bool] = ContextVar("_in_process_live", default=False)


def check_not_in_process_live(api_name: str) -> None:
    """Raise if called from inside `process_live`.

    Called by `coco.mount`, `coco.mount_each`, `coco.use_mount` at entry.
    Outside of `process_live`, `_in_process_live.get()` returns the
    default `False` (e.g. inside `process()` after `update_full`'s
    inline reset, or in any non-live-component context).
    """
    if _in_process_live.get():
        raise RuntimeError(
            f"{api_name}() is not allowed inside process_live; "
            f"use operator.update / operator.delete instead. "
            f"(See specs/live_component/requirement.md for the rationale.)"
        )


async def _process_live_wrapper(instance: Any, operator: LiveComponentOperator) -> None:
    """Wrap a `process_live` invocation to set `_in_process_live = True`
    and detach the operator's controller on exit.

    Used by `_mount_live_component` (api.py) and the LiveCompClass branch
    of `LiveComponentOperator.update`.

    Live components run independently from the parent update, so parent
    cooperative deadlines do not apply to ``process_live`` or the
    ``operator.update_full()`` calls it starts.

    Save/restore the prior value rather than using `ContextVar.reset(token)`:

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Replace mount/use_mount/mount_each inside process_live with operator.update / operator.delete on the live component operator.
  2. Move one-time static mounting outside the process_live body (e.g. in the lifespan/setup).
  3. Consult specs/live_component/requirement.md for the required live-component pattern.

Example fix

// before
async def body(op):
    await coco.mount(process_file, f, target)
// after
async def body(op):
    await op.update(key, fn, args)
Defensive patterns

Strategy: validation

Validate before calling

from cocoindex._internal.live_component import _in_process_live
if _in_process_live.get():
    raise RuntimeError("use operator.update/delete inside process_live")

Try / catch

try:
    await coco.mount(fn, *args)
except RuntimeError as e:
    if "process_live" in str(e):
        await operator.update(key, fn, args)

Prevention

When it happens

Trigger: Calling coco.mount(...), coco.mount_each(...) or coco.use_mount(...) inside the async body passed to process_live.

Common situations: Porting an existing update()-style main function into a live component body without replacing mount calls with operator updates; mixing full-scan mounting with incremental live semantics in the same component.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/f14c15adb3646e3e. Report an issue: GitHub.