cocoindex-io/cocoindex · error · ValueError

Batching and runner require the function to be async. Use @c

Error message

Batching and runner require the function to be async. Use @coco.fn.as_async instead, or rewrite the function to be async.

What it means

The sync builder path (_build_sync, used by @coco.fn for plain sync functions) cannot support batching= or runner=, which require asynchronous execution. Applying such a decorator configuration to a sync function raises ValueError at decoration time.

Source

Thrown at python/cocoindex/_internal/function.py:1850

        batching: bool = False,
        max_batch_size: int | None = None,
        runner: Runner | None = None,
        version: int | None = None,
        logic_tracking: LogicTracking = "full",
        deps: Any = None,
    ) -> None:
        self._memo = memo
        self._memo_key = memo_key
        self._batching = batching
        self._max_batch_size = max_batch_size
        self._runner = runner
        self._version = version
        self._logic_tracking = logic_tracking
        self._deps = deps

    def _build_sync(self, fn: Callable[P, R_co]) -> SyncFunction[P, R_co]:
        if self._batching or self._runner is not None:
            raise ValueError(
                "Batching and runner require the function to be async. "
                "Use @coco.fn.as_async instead, or rewrite the function to be async."
            )
        wrapper = SyncFunction(
            fn,
            memo=self._memo,
            memo_key=self._memo_key,
            version=self._version,
            logic_tracking=self._logic_tracking,
            deps=self._deps,
        )
        functools.update_wrapper(wrapper, fn)
        return wrapper

    def _build_async(
        self,
        fn: AnyCallable[P, R_co],
    ) -> AsyncFunction[P, R_co]:

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Rewrite the function as async def and use @coco.fn.as_async.
  2. Remove batching=/runner= options if the function should stay sync.
  3. Keep the function sync and process batches externally with mount_each/map instead.

Example fix

// before
@coco.fn(batching=True)
def embed(texts): ...
// after
@coco.fn.as_async(batching=True)
async def embed(texts): ...
Defensive patterns

Strategy: validation

Validate before calling

import inspect
if (batching or runner is not None) and not inspect.iscoroutinefunction(fn):
    raise TypeError("use @coco.fn.as_async for batching/runner")

Type guard

def is_async_fn(fn): return inspect.iscoroutinefunction(fn)

Try / catch

try:
    deco = coco.fn(batching=True)(fn)
except ValueError:
    deco = coco.fn.as_async(batching=True)(make_async(fn))

Prevention

When it happens

Trigger: @coco.fn(batching=True) or @coco.fn(runner=...) applied to a def (non-async) function; _build_sync invoked via __call__ with _batching set or _runner non-None.

Common situations: Copying a decorator config that used batching onto a sync function; switching a function from async def to def while keeping batching/runner options; using @coco.fn instead of @coco.fn.as_async for batched processing.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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