roboflow/supervision · error · ValueError

Callback must return `list[Detections]` when `batch_size > 1

Error message

Callback must return `list[Detections]` when `batch_size > 1`. Got: {type(detections_in_slices)}

What it means

Raised by InferenceSlicer's batch path when the callback returns something other than a list while batch_size > 1. In batch mode the callback receives a list of image slices and must return a list with exactly one Detections object per slice; a single Detections (the single-image contract) or any other type cannot be aligned with the slices.

Source

Thrown at src/supervision/detection/tools/inference_slicer.py:586

        if _is_windowed_raster(image):
            slices = []
            for offset in offsets:
                x_min, y_min, x_max, y_max = (int(v) for v in offset)
                window = ((y_min, y_max), (x_min, x_max))
                with self._raster_read_lock:
                    bands = image.read(window=window)
                slices.append(np.ascontiguousarray(np.transpose(bands, (1, 2, 0))))
            resolution_wh = (image.width, image.height)
        else:
            slices = [crop_image(image=image, xyxy=offset) for offset in offsets]
            resolution_wh = get_image_resolution_wh(image)

        batch_callback = cast(
            Callable[[list[npt.NDArray[Any]]], list[Detections]], self.callback
        )
        detections_in_slices = batch_callback(slices)
        if not isinstance(detections_in_slices, list):
            raise ValueError(
                "Callback must return `list[Detections]` when `batch_size > 1`. "
                f"Got: {type(detections_in_slices)}"
            )
        if len(detections_in_slices) != len(offsets):
            raise ValueError(
                f"Callback returned {len(detections_in_slices)} Detections "
                f"for {len(offsets)} slices. Lengths must match."
            )

        if self.compact_masks:
            for det, image_slice in zip(detections_in_slices, slices):
                if det.mask is not None and isinstance(det.mask, np.ndarray):
                    slice_w, slice_h = get_image_resolution_wh(image_slice)
                    full_slice_xyxy = np.tile(
                        np.array([[0, 0, slice_w - 1, slice_h - 1]], dtype=np.float64),
                        (len(det), 1),
                    )
                    det.mask = CompactMask.from_dense(

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Rewrite the callback for batching: accept a list of images, return [sv.Detections.from_ultralytics(r) for r in model.predict(images, ...)] — one entry per input image, in order.
  2. Alternatively keep the single-image callback and set batch_size=1 (the default).
  3. Ensure the list length equals the input length — see the companion length-mismatch error.

Example fix

# before
def callback(image):
    return sv.Detections.from_ultralytics(model.predict(image, verbose=False)[0])
slicer = sv.InferenceSlicer(callback=callback, batch_size=8)

# after
def callback(images):
    results = model.predict(images, verbose=False)
    return [sv.Detections.from_ultralytics(r) for r in results]
slicer = sv.InferenceSlicer(callback=callback, batch_size=8)
Defensive patterns

Strategy: type-guard

Validate before calling

def is_batch_callback(cb, n=2) -> bool:
    import numpy as np
    probe = [np.zeros((8, 8, 3), dtype=np.uint8) for _ in range(n)]
    result = cb(probe)
    return isinstance(result, list) and len(result) == n

if batch_size > 1 and not is_batch_callback(callback):
    batch_size = 1
slicer = sv.InferenceSlicer(callback=callback, batch_size=batch_size)

Type guard

def returns_detections_list(fn) -> bool:
    # static check on a probe call with dummy images
    probe = fn([np.zeros((4, 4, 3), dtype=np.uint8)] * 2)
    return isinstance(probe, list)

Try / catch

try:
    detections = slicer(image)
except ValueError as err:
    if 'list[Detections]' in str(err):
        raise RuntimeError('callback must accept and return a list when batch_size > 1') from err
    raise

Prevention

When it happens

Trigger: Constructing InferenceSlicer with batch_size=4 but a callback of the form def callback(image) -> sv.Detections (single-image signature); the slicer calls it with a list and the raw non-list result hits this check.

Common situations: Upgrading a working single-image pipeline to batching without rewriting the callback; wrapping an ultralytics model.predict call that returns a Results list but converting only the first element.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/cb189b0f19cb6b99. Report an issue: GitHub.