roboflow/supervision · error · ValueError

COCO RLE counts cannot be empty.

Error message

COCO RLE counts cannot be empty.

What it means

Raised when the COCO RLE counts array parsed for CompactMask.from_coco_rle is empty (size 0). An RLE must contain at least one run count to describe a mask, and the code uses counts to verify total area, so an empty array is meaningless and rejected. This check runs after the one-dimensionality check.

Source

Thrown at src/supervision/detection/compact_mask.py:411

        else:
            # Convert to int64 first, then range-check against int32 bounds before
            # narrowing. A direct int32 cast wraps silently on some numpy versions
            # and raises on others; this makes overflow detection deterministic.
            counts_arr64 = np.asarray(counts, dtype=np.int64)
            int32_info = np.iinfo(np.int32)
            if counts_arr64.size and (
                counts_arr64.max() > int32_info.max
                or counts_arr64.min() < int32_info.min
            ):
                raise ValueError("COCO RLE counts exceed int32 range.")
            counts_arr = counts_arr64.astype(np.int32)
    except (TypeError, ValueError, OverflowError) as exc:
        raise ValueError("Invalid COCO RLE counts.") from exc

    if counts_arr.ndim != 1:
        raise ValueError("COCO RLE counts must be one-dimensional.")
    if counts_arr.size == 0:
        raise ValueError("COCO RLE counts cannot be empty.")
    if np.any(counts_arr < 0):
        raise ValueError("COCO RLE counts must be non-negative.")
    return counts_arr


def _rle_resize(
    rle: npt.NDArray[np.int32],
    crop_h: int,
    crop_w: int,
    new_crop_h: int,
    new_crop_w: int,
) -> npt.NDArray[np.int32]:
    """Resize an F-order RLE-encoded crop via nearest-neighbour resampling.

    Manipulates run lengths directly without decoding to a full 2D boolean
    array.  Delegates to :func:`_rle_split_cols`, :func:`_rle_scale_col`,
    and :func:`_rle_join_cols`.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. If the annotation genuinely has no mask, skip it instead of passing an empty RLE — filter annotations where not ann.get('segmentation').
  2. If a mask exists, generate a correct RLE with pycocotools.mask.encode on the binary mask so counts is populated.
  3. Check the JSON source for truncation if counts should not be empty.

Example fix

# before
rles = [{"size": [4, 4], "counts": []}]

# after (skip empty masks)
rles = [r for r in coco_rles if len(r["counts"]) > 0]
Defensive patterns

Strategy: validation

Validate before calling

rles = [r for r in rles if len(r.get("counts", [])) > 0]
# or for compressed payloads: if r.get("counts")]

Type guard

def has_nonempty_counts(rle) -> bool:
    c = rle.get("counts")
    return bool(c) and (not isinstance(c, (list, tuple)) or len(c) > 0)

Try / catch

try:
    cm = sv.CompactMask.from_coco_rle(rles, xyxy, image_shape=shape)
except ValueError as e:
    if "cannot be empty" in str(e):
        rles = [r for r in rles if r["counts"]]
        cm = sv.CompactMask.from_coco_rle(rles, xyxy, image_shape=shape)
    else:
        raise

Prevention

When it happens

Trigger: Passing rle['counts'] = [] or an empty numpy array to CompactMask.from_coco_rle; or a COCO annotation whose segmentation counts field is an empty list/string.

Common situations: Placeholder annotations created for empty masks (COCO uses iscrowd/empty segmentation for no mask); truncated JSON; a serializer that dropped the counts field content; building RLEs programmatically and forgetting to fill counts.

Related errors


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