roboflow/supervision · error · ValueError
mode must be 'edge' or 'centroid'
Error message
mode must be 'edge' or 'centroid'
What it means
The mask cleanup helper supports exactly two distance modes: 'centroid' (distance between component centroids) and 'edge' (distance between component edges via Chamfer-style distances). Any other string — or a non-string value — reaches the final else branch and raises.
Source
Thrown at src/supervision/detection/utils/masks.py:512
main_mask = labels == main_label
if np.isnan(threshold) or threshold < 0:
nearby_main = np.zeros_like(main_mask)
elif np.isposinf(threshold):
nearby_main = np.ones_like(main_mask)
else:
fixed_distances = _chamfer_distances(main_mask)
distances = fixed_distances.astype(np.float32) / 65536
nearby_main = distances <= threshold
for label in range(1, num_labels):
if label == main_label:
continue
component = labels == label
if not np.any(component):
continue
if np.any(nearby_main & component):
keep_labels[label] = True
else:
raise ValueError("mode must be 'edge' or 'centroid'")
return keep_labels[labels]
def mask_to_roi(mask: npt.NDArray[np.bool_]) -> tuple[int, int, int, int] | None:
"""Return exclusive ``(x1, y1, x2, y2)`` bounds for true mask pixels.
Use this helper when you need NumPy slice semantics. Unlike
:func:`~supervision.detection.utils.converters.mask_to_xyxy`, this
function uses exclusive upper bounds (``+1``) and returns ``None`` for
empty masks instead of zeros. The inclusive ``mask_to_xyxy`` convention
stays in place for compatibility with CompactMask and box-based adapters.
Args:
mask: 2D boolean array of shape ``(H, W)``.
Returns:
Exclusive ``(x1, y1, x2, y2)`` bounds, or ``None`` when the maskView on GitHub (pinned to 7f254d9784)
Solutions
- Use exactly 'centroid' or 'edge' (lowercase).
- Normalize config values: mode = cfg['mode'].strip().lower().
- Validate mode at the config boundary with an explicit error listing the two options.
Example fix
# before filtered = filter_non_zero_mask_areas(mask=m, relative_distance=0.2, mode="center") # after filtered = filter_non_zero_mask_areas(mask=m, relative_distance=0.2, mode="centroid")
Defensive patterns
Strategy: validation
Validate before calling
mode = mode.strip().lower()
if mode not in ("edge", "centroid"):
raise ValueError(f"mode must be 'edge' or 'centroid', got {mode!r}") Type guard
def is_valid_mode(mode: str) -> bool:
return mode.strip().lower() in ("edge", "centroid") Prevention
- Normalize mode strings from config with strip().lower().
- Remember 'centroid' is the exact spelling, not 'center'.
- Expose only the two valid values in your own CLI/config schema.
When it happens
Trigger: Passing mode='center' (common typo for 'centroid'), mode='Edge' (case-sensitive), or a config value with whitespace like 'edge ' read from a file.
Common situations: Config/CLI values not normalized (case, whitespace); assuming sklearn-style naming ('distance'); typos between 'centroid' and 'center'.
Related errors
- Incorrect connectivity value. Possible connectivity values:
- Either absolute_distance or relative_distance must be set.
- coordinate_convention must be 'inclusive' or 'exclusive', go
- masks_true and masks_detection must be 3D (N, H, W); got ndi
- MeanAverageRecall with `MetricTarget.MASKS` requires detecti
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/b6094eeefcd15585.
Report an issue: GitHub.