roboflow/supervision · error · ValueError

COCO RLE counts must be non-negative.

Error message

COCO RLE counts must be non-negative.

What it means

Raised when any element of the COCO RLE counts array is negative. RLE counts encode run lengths of alternating background/foreground pixels and are, by definition, non-negative. A negative value indicates malformed or corrupt RLE data, and the parser rejects it before attempting to reconstruct the mask.

Source

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

            # 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`.

    The nearest-neighbour mapping ``src = floor(dst * src_size / dst_size)``
    is bit-exact with ``cv2.INTER_NEAREST``.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Regenerate the RLE from a trusted source: build the boolean mask and use pycocotools.mask.encode to get valid counts.
  2. If decoding compressed counts yourself, verify the decoder (delta/Base48 paths) against pycocotools output before feeding from_coco_rle.
  3. Sanity-check counts with (np.asarray(counts) >= 0).all() before the call.

Example fix

# before
counts = [0, -2, 6, 8]  # negative run

# after — regenerate from a mask
from pycocotools import mask as mask_utils
rle = mask_utils.encode(np.asfortranarray(mask.astype(np.uint8)))
counts = list(rle["counts"]) if isinstance(rle["counts"], list) else rle  # use encoded payload
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
assert (np.asarray(rle["counts"], dtype=np.int64) >= 0).all(), "negative RLE count"

Try / catch

try:
    cm = sv.CompactMask.from_coco_rle(rles, xyxy, image_shape=shape)
except ValueError as e:
    if "non-negative" in str(e):
        raise ValueError(f"corrupt RLE for mask, counts={rle['counts']}") from e
    raise

Prevention

When it happens

Trigger: Passing counts like [0, -2, 6, 8] to CompactMask.from_coco_rle; delta-decoding logic upstream that produced negatives; hand-edited or corrupted annotation JSON.

Common situations: Custom RLE encoders with off-by-one bugs producing -1 runs; JSON corruption; incorrectly ported compressed-RLE decoders; LLM/script-generated annotation data.

Related errors


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