pathwaycom/pathway · error · ValueError

A batch UDF has to return a list but is annotated as returni

Error message

A batch UDF has to return a list but is annotated as returning {sig_return_type}

What it means

ValueError raised when a batched UDF (max_batch_size set) relies on its signature for the return type, but the annotation is not a list. For batch mode each argument is a list and the function must return a list, so the signature must be annotated as list[T] (or List[T]); Pathway then unwraps T as the element type.

Source

Thrown at python/pathway/internals/udfs/__init__.py:206

            wrapped_sig_return_type is None
            or (
                sig_return_type != Any
                and not dt.dtype_issubclass(
                    wrapped_sig_return_type, dt.wrap(return_type)
                )
            )
        ):
            warn(
                f"The value of return_type parameter ({return_type}) is inconsistent with"
                + f" UDF's return type annotation ({sig_return_type}).",
                stacklevel=3,
            )
        if return_type is ...:  # return type only specified in signature
            if self.max_batch_size is None:
                return sig_return_type
            else:
                if not isinstance(wrapped_sig_return_type, dt.List):
                    raise ValueError(
                        f"A batch UDF has to return a list but is annotated as returning {sig_return_type}"
                    )
                return wrapped_sig_return_type.wrapped

        return return_type

    def _wrap_function(self) -> Callable:
        func = self.executor._wrap(self.__wrapped__)
        if self.cache_strategy is not None:
            func = with_cache_strategy(func, self.cache_strategy)
        return func

    def _prepare_executor(self, executor: Executor) -> Executor:
        is_coroutine = inspect.iscoroutinefunction(self.__wrapped__)
        if is_coroutine and isinstance(executor, SyncExecutor):
            raise ValueError("The function is a coroutine. You can't use SyncExecutor.")
        if isinstance(executor, AutoExecutor):
            return async_executor() if is_coroutine else udfs.sync_executor()

View on GitHub (pinned to fa2f74a464)

Solutions

  1. Annotate the batched UDF as returning list[ElementType], e.g. -> list[int]
  2. Alternatively pass return_type=int explicitly (return_type stays the element type even in batch mode)
  3. Ensure the function body actually builds and returns a list of the same length as the inputs

Example fix

// before
@pw.udf(max_batch_size=100)
def f(x: list[int]) -> int:
    return [v * 2 for v in x]

// after
@pw.udf(max_batch_size=100)
def f(x: list[int]) -> list[int]:
    return [v * 2 for v in x]
Defensive patterns

Strategy: type-guard

Validate before calling

import inspect, typing

def batch_signature_ok(fn) -> bool:
    sig = inspect.signature(fn)
    rt = sig.return_annotation
    origin = typing.get_origin(rt)
    return origin in (list, typing.List)

Type guard

import typing

def is_list_annotation(ann) -> bool:
    return typing.get_origin(ann) in (list, typing.List)

Prevention

When it happens

Trigger: @pw.udf(max_batch_size=...) def f(x: list[int]) -> int (annotated as returning int, not list[int]); any batched UDF whose -> annotation is a scalar type, Optional, or bare typing construct that does not resolve to dt.List.

Common situations: Developer converts a row-wise UDF to batch mode and forgets to change the return annotation from T to list[T]; mixing return_type parameter (which should stay T) with signature annotation conventions.

Related errors


AI-assisted analysis of pathwaycom/pathway@fa2f74a464 (2026-08-15). Data as JSON: /api/errors/011e7d6810409a73. Report an issue: GitHub.