cocoindex-io/cocoindex · error · ValueError
Async functions are not supported by @coco.fn decorator when
Error message
Async functions are not supported by @coco.fn decorator when batching or runner is specified. Please use @coco.fn.as_async instead.
What it means
_SyncFunctionBuilder only supports sync functions; when batching or runner is configured and an async def function is passed, it raises because async functions must go through the async builder (@coco.fn.as_async).
Source
Thrown at python/cocoindex/_internal/function.py:1892
sync_fn,
memo=self._memo,
memo_key=self._memo_key,
batching=self._batching,
max_batch_size=self._max_batch_size,
runner=self._runner,
version=self._version,
logic_tracking=self._logic_tracking,
deps=self._deps,
)
functools.update_wrapper(wrapper, fn)
return wrapper
# Only supports sync function -> sync function
class _SyncFunctionBuilder(_GenericFunctionBuilder):
def __call__(self, fn: Callable[P, R_co]) -> SyncFunction[P, R_co]:
if inspect.iscoroutinefunction(fn):
raise ValueError(
"Async functions are not supported by @coco.fn decorator "
"when batching or runner is specified. "
"Please use @coco.fn.as_async instead."
)
return self._build_sync(fn)
# Supports sync function -> sync function and async function -> async function
class _AutoFunctionBuilder(_GenericFunctionBuilder):
def __init__(
self,
*,
memo: bool = False,
memo_key: MemoKeySpec = None,
version: int | None = None,
logic_tracking: LogicTracking = "full",
deps: Any = None,
) -> None:View on GitHub (pinned to e84aa99b32)
Solutions
- Use @coco.fn.as_async(batching=True) (or with runner=) instead of @coco.fn.
- Or make the function synchronous def if it performs no awaiting, keeping @coco.fn.
- Drop batching/runner if plain per-item async processing is intended.
Example fix
// before @coco.fn(batching=True) async def embed(texts): ... // after @coco.fn.as_async(batching=True) async def embed(texts): ...
Defensive patterns
Strategy: validation
Validate before calling
import inspect
if inspect.iscoroutinefunction(fn) and (batching or runner is not None):
deco = coco.fn.as_async # pick async builder up front Type guard
def is_async_fn(fn): return inspect.iscoroutinefunction(fn)
Try / catch
try:
return coco.fn(batching=True)(fn)
except ValueError:
return coco.fn.as_async(batching=True)(fn) Prevention
- Choose the decorator based on inspect.iscoroutinefunction before applying options.
- Search examples with batching to confirm they use coco.fn.as_async.
When it happens
Trigger: Applying @coco.fn(batching=True) or @coco.fn(runner=...) to an async def function — inspect.iscoroutinefunction(fn) is True so __call__ raises before building.
Common situations: Naturally writing the processor as async def (common for I/O-bound work like embedding calls) but reaching for plain @coco.fn with batching options copied from an example.
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
- Batching and runner require the function to be async. Use @c
- Either async_fn or sync_fn must be provided
- Expected at least one input argument
- @serialize_by_pickle on {cls.__qualname__} (a {'dataclass' i
- use_mount() requires a ComponentSubpath when the function ha
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/0eb5570a081fdd8a.
Report an issue: GitHub.