cocoindex-io/cocoindex · error · TypeError

watch() is not supported when preview=True

Error message

watch() is not supported when preview=True

What it means

App.watch() streams live progress updates as incremental processing happens. When the App was configured with preview=True it runs a one-shot preview and produces no continuous change stream, so watch() is meaningless and raises TypeError.

Solutions

  1. Set preview=False in the App config, then call watch()
  2. For preview runs, await app.update() and read the final snapshot instead of watching
  3. Create a separate non-preview App instance for watching

Example fix

// before
app = coco.App(coco.AppConfig(name="demo", preview=True), main)
async for snap in app.watch(): ...
// after
app = coco.App(coco.AppConfig(name="demo"), main)
async for snap in app.watch(): ...
Defensive patterns

Strategy: validation

Validate before calling

if not app._preview:
    async for snap in app.watch():
        ...
else:
    await app.update()  # preview: one-shot only

Type guard

def supports_watch(app) -> bool:
    return not getattr(app, "_preview", False)

Try / catch

try:
    async for snap in app.watch():
        ...
except TypeError as e:
    if "preview=True" in str(e):
        await app.update()  # fall back to one-shot preview result

Prevention

When it happens

Trigger: Calling app.watch() (directly or via _drive_to_ready) on an App constructed with coco.AppConfig(..., preview=True) or preview=True passed to the App.

Common situations: Reusing a preview-mode App for a long-running watcher; toggling preview on for debugging and forgetting it before adding watch(); copying example watch() code into a preview app.

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/f026b89f18b46b4e. Report an issue: GitHub.

Appendix: source

Thrown at python/cocoindex/_internal/app.py:105

        if self._core_handle is None:
            return None
        return self._snapshot_from_handle(self._core_handle).stats

    async def watch(self) -> AsyncIterator[UpdateSnapshot[R]]:
        """Async iterator that yields progress snapshots.

        Yields UpdateSnapshot with status:
        - RUNNING while the update is in progress (not yet ready)
        - READY when the root component is ready (initial processing caught up)

        In live mode, after the initial READY, continues yielding RUNNING snapshots
        as stats update from incremental processing. When terminated, yields a final
        READY snapshot with the result set.

        On error, raises the exception directly from the iterator.
        """
        if self._preview:
            raise TypeError("watch() is not supported when preview=True")
        handle = await self._ensure_started()
        last_version = 0
        while True:
            version = await handle.changed()

            # Check termination before dedup — notify_terminated() sends
            # TERMINATED_VERSION on the watch channel without updating the
            # stats version, so the dedup check would skip it.
            if version >= _TERMINATED_VERSION:
                snap = self._snapshot_from_handle(handle)
                pyvalue: Any = await handle.result()
                result: R = pyvalue.get(fn_ret_deserializer(self._main_fn))
                if snap.stats is not None:
                    yield UpdateSnapshot(
                        stats=snap.stats, status=UpdateStatus.READY, result=result
                    )
                return

View on GitHub (pinned to e84aa99b32)