roboflow/supervision · error · ValueError
Input mask must be 2D
Error message
Input mask must be 2D
What it means
mask_to_rle encodes a single 2D binary mask (H x W) into alternating run counts; a 3D array (e.g. a batch of masks with shape (N, H, W)) or a 1D vector has no well-defined scan order for a single mask, so ndim != 2 raises immediately.
Source
Thrown at src/supervision/detection/utils/converters.py:792
... [False, False, False, False],
... [False, True, True, False],
... [False, True, True, False],
... [False, False, False, False],
... ])
>>> rle = sv.mask_to_rle(mask)
>>> [int(x) for x in rle]
[5, 2, 2, 2, 5]
>>> sv.mask_to_rle(mask, compressed=True)
'52203'
```
{ align=center width="800" }
"""
if mask.ndim != 2:
raise ValueError("Input mask must be 2D")
if mask.size == 0:
raise ValueError("Input mask cannot be empty")
counts: list[int] = cast(list[int], _mask_to_rle_counts(mask).tolist())
if compressed:
return _base48_encode(_delta_encode(counts))
return counts
def polygon_to_xyxy(polygon: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
"""
Converts a polygon represented by a NumPy array into a bounding box.
Args:
polygon: A polygon represented by a NumPy array of shape `(N, 2)`,
containing the `x`, `y` coordinates of the points.
Returns:View on GitHub (pinned to 7f254d9784)
Solutions
- Index one mask from the batch: mask_to_rle(detections.mask[i]).
- Squeeze spurious dims right before the call: np.squeeze(mask) then assert .ndim == 2.
- For a (H, W, 1) tensor conversion, use [..., 0].
Example fix
# before rles = [sv.mask_to_rle(m) for m in detections.mask] # after rles = [sv.mask_to_rle(detections.mask[i]) for i in range(len(detections))]
Defensive patterns
Strategy: type-guard
Validate before calling
assert mask.ndim == 2, f"expected 2D mask, got shape {mask.shape}" Type guard
def is_single_2d_mask(mask: npt.NDArray) -> bool:
return mask.ndim == 2 Prevention
- Index the batch: detections.mask[i], never pass the (N, H, W) array.
- After tensor -> numpy conversions, assert ndim before encoding.
- Squeeze channel dims at the pipeline boundary.
When it happens
Trigger: Passing detections.mask (shape (N, H, W)) directly instead of one slice detections.mask[i]; passing a single row/column vector; passing a mask with a trailing channel dimension (H, W, 1) from a tensor conversion.
Common situations: Iterating batches and forgetting to index; converting torch tensors with .numpy() that keep a batch or channel dim; feeding model output directly.
Related errors
- the sum of the number of pixels in the RLE must be the same
- Input mask cannot be empty
- visible must be a 2D np.ndarray with shape (n, m), but got s
- visible first dimension must be {n}, but got shape {actual_s
- visible second dimension must be {m}, but got shape {actual_
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/8fcf1003c07fc5ab.
Report an issue: GitHub.