cocoindex-io/cocoindex · error · ValueError

Either async_fn or sync_fn must be provided

Error message

Either async_fn or sync_fn must be provided

What it means

The @coco.fn / Function wrapper constructor requires at least one callable: either an async function (async_fn) or a sync function (sync_fn). It is thrown when both are None, which can only happen if the caller explicitly passes None or constructs the builder/wrapper without supplying a function.

Source

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

    _batchers_lock: threading.Lock

    def __init__(
        self,
        async_fn: AsyncCallable[..., Any] | None,
        sync_fn: Callable[..., Any] | None,
        *,
        memo: bool,
        memo_key: MemoKeySpec = None,
        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:
        fn = async_fn or sync_fn
        if fn is None:
            raise ValueError("Either async_fn or sync_fn must be provided")
        if logic_tracking is None and deps is not None:
            raise ValueError(
                "deps= requires logic_tracking to be enabled; with "
                "logic_tracking=None the function's logic is not tracked at "
                "all, so the deps value would be silently ignored."
            )
        self._orig_async_fn = async_fn
        self._orig_sync_fn = sync_fn
        self._memo = memo
        self._memo_key = _normalize_memo_key(fn, memo_key)
        self._processor_info = core.ComponentProcessorInfo(fn.__qualname__)
        self._logic_tracking = logic_tracking
        self._return_deserializer = None
        self._return_deserializer_lock = threading.Lock()

        if logic_tracking is not None:
            self._logic_fp = _compute_logic_fingerprint(fn, version=version, deps=deps)
            core.register_logic_fingerprint(self._logic_fp)

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Pass the decorated function to @coco.fn (or as_async/sync variant) instead of None.
  2. If constructing programmatically, assert the callable is not None before building the Fn wrapper.
  3. Check kwarg spelling: the fn must arrive as async_fn= or sync_fn=, not another name.

Example fix

// before
coco.fn(async_fn=None, sync_fn=None, memo=True)
// after
coco.fn(async_fn=my_async_fn, memo=True)
Defensive patterns

Strategy: validation

Validate before calling

if async_fn is None and sync_fn is None:
    raise ValueError("one of async_fn/sync_fn must be a callable")

Type guard

callable(async_fn) or callable(sync_fn)

Try / catch

try:
    wrapped = coco.fn(async_fn=f)
except ValueError:
    wrapped = None

Prevention

When it happens

Trigger: Calling Function.__init__ (or a low-level builder path) with async_fn=None and sync_fn=None, e.g. Fn(async_fn=None, sync_fn=None) or programmatic construction that forwards a variable that is None.

Common situations: Programmatic/metaprogramming use of coco.fn internals where the function handle is resolved dynamically (e.g. from config or a registry miss) and ends up None; typos passing the fn as an unrecognized kwarg so it is dropped and defaults to None.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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