{"record":{"id":"0eb5570a081fdd8a","repo":"cocoindex-io/cocoindex","slug":"async-functions-are-not-supported-by-coco-fn-deco","errorCode":null,"errorMessage":"Async functions are not supported by @coco.fn decorator when batching or runner is specified. Please use @coco.fn.as_async instead.","messagePattern":"Async functions are not supported by @coco\\.fn decorator when batching or runner is specified\\. Please use @coco\\.fn\\.as_async instead\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/function.py","lineNumber":1892,"sourceCode":"            sync_fn,\n            memo=self._memo,\n            memo_key=self._memo_key,\n            batching=self._batching,\n            max_batch_size=self._max_batch_size,\n            runner=self._runner,\n            version=self._version,\n            logic_tracking=self._logic_tracking,\n            deps=self._deps,\n        )\n        functools.update_wrapper(wrapper, fn)\n        return wrapper\n\n\n# Only supports sync function -> sync function\nclass _SyncFunctionBuilder(_GenericFunctionBuilder):\n    def __call__(self, fn: Callable[P, R_co]) -> SyncFunction[P, R_co]:\n        if inspect.iscoroutinefunction(fn):\n            raise ValueError(\n                \"Async functions are not supported by @coco.fn decorator \"\n                \"when batching or runner is specified. \"\n                \"Please use @coco.fn.as_async instead.\"\n            )\n        return self._build_sync(fn)\n\n\n# Supports sync function -> sync function and async function -> async function\nclass _AutoFunctionBuilder(_GenericFunctionBuilder):\n    def __init__(\n        self,\n        *,\n        memo: bool = False,\n        memo_key: MemoKeySpec = None,\n        version: int | None = None,\n        logic_tracking: LogicTracking = \"full\",\n        deps: Any = None,\n    ) -> None:","sourceCodeStart":1874,"sourceCodeEnd":1910,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/function.py#L1874-L1910","documentation":"_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).","triggerScenarios":"Applying @coco.fn(batching=True) or @coco.fn(runner=...) to an async def function — inspect.iscoroutinefunction(fn) is True so __call__ raises before building.","commonSituations":"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.","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."],"exampleFix":"// before\n@coco.fn(batching=True)\nasync def embed(texts): ...\n// after\n@coco.fn.as_async(batching=True)\nasync def embed(texts): ...","handlingStrategy":"validation","validationCode":"import inspect\nif inspect.iscoroutinefunction(fn) and (batching or runner is not None):\n    deco = coco.fn.as_async  # pick async builder up front","typeGuard":"def is_async_fn(fn): return inspect.iscoroutinefunction(fn)","tryCatchPattern":"try:\n    return coco.fn(batching=True)(fn)\nexcept ValueError:\n    return coco.fn.as_async(batching=True)(fn)","preventionTips":["Choose the decorator based on inspect.iscoroutinefunction before applying options.","Search examples with batching to confirm they use coco.fn.as_async."],"tags":["python","async","decorator","batching"],"backgroundTag":"invalid-argument-value","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}