cocoindex-io/cocoindex · error · RuntimeError

{self._label} supports a single active watch() at a time.

Error message

{self._label} supports a single active watch() at a time.

What it means

The connectorkits watch() guard enforces that only one active watch session exists per labeled resource at a time; entering the context manager twice concurrently would produce duplicated or interleaved event streams, so it raises this RuntimeError.

Source

Thrown at python/cocoindex/connectorkits/__init__.py:50

    The flag resets on every exit — normal return, exception, or cancellation (a
    cancelled ``await`` inside the ``with`` unwinds through ``__exit__``) — so the
    feed can be re-watched sequentially after a prior ``watch()`` finishes.

    A plain flag (no lock) suffices when ``watch()`` runs entirely on one event
    loop, as the framework's live consumer does. A feed that already carries
    equivalent "is being watched" state can guard on that instead.
    """

    __slots__ = ("_label", "_active")

    def __init__(self, label: str) -> None:
        self._label = label
        self._active = False

    def __enter__(self) -> None:
        if self._active:
            raise RuntimeError(
                f"{self._label} supports a single active watch() at a time."
            )
        self._active = True

    def __exit__(self, *exc: object) -> None:
        self._active = False


def default_subpath_name(processor_fn: Any) -> str | None:
    """Resolve the default subpath name for a mount target.

    Honors an explicit ``__coco_subpath_name__`` attribute (set by wrappers
    like ``coco.auto_refresh`` so the wrapper class can keep an honest
    ``__name__`` while still mounting under the wrapped function's name),
    falling back to ``__name__``.
    """
    name = getattr(processor_fn, "__coco_subpath_name__", None)
    if isinstance(name, str):

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Exit the first watch() before entering another (restructure into sequential with-blocks)
  2. Create a separate watch handle/instance for each concurrent consumer instead of sharing one
  3. Serialize watch usage in the same task/thread, or fan out events from a single watch to multiple consumers
  4. Verify no leaked context due to an exception path that skips __exit__ (use try/finally or with-statements)

Example fix

// before
w1 = res.watch(); w2 = res.watch()
with w1, w2:  # RuntimeError
// after
with res.watch():
    handle_events()
Defensive patterns

Strategy: try-catch

Validate before calling

if watch_handle._active:
    raise RuntimeError("watch already active; exit it before re-entering")

Try / catch

try:
    with resource.watch():
        consume()
except RuntimeError as e:
    if "single active watch" in str(e):
        serialize_watch_usage()  # retry sequentially

Prevention

When it happens

Trigger: Entering the same watch() context manager a second time while a previous one is still active (not __exit__-ed) — e.g. nesting two `with resource.watch():` blocks on the same object, or re-entering from another thread/task before exit.

Common situations: Refactoring code that calls watch() in two helper functions both entered simultaneously; spawning concurrent tasks that each enter the same watch context; forgetting to exit a watch before starting another (exception in the body leaving it open is handled by __exit__, but manual misuse is not).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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