roboflow/supervision · error · ValueError

Percentage must be in the range [0, 1).

Error message

Percentage must be in the range [0, 1).

What it means

`sv.approximate_polygon(polygon, percentage, epsilon_step)` simplifies a polygon by removing a fraction of its points via Ramer-Douglas-Peucker; `percentage` is that fraction and must lie in [0, 1). This ValueError fires for negative values or values >= 1 — a percentage of 1 would mean removing all points (a polygon needs at least 3), and negative removal is meaningless. Note the value is a fraction, not 0-100.

Source

Thrown at src/supervision/detection/utils/polygons.py:112

        >>> polygon = np.array([[0, 0], [10, 0], [10, 10], [0, 10],
        ...                     [5, 10], [5, 5], [3, 7], [1, 9]])
        >>> result = approximate_polygon(polygon, percentage=0.5)
        >>> result.shape[1]
        2
        >>> len(result) <= max(int(len(polygon) * 0.5), 3)
        True

        Polygon already at or below target — returned unchanged:

        >>> tiny = np.array([[0, 0], [5, 0], [2, 4]])
        >>> approximate_polygon(tiny, percentage=0.5) is tiny
        True

        ```
    """

    if percentage < 0 or percentage >= 1:
        raise ValueError("Percentage must be in the range [0, 1).")
    if epsilon_step <= 0:
        raise ValueError("epsilon_step must be positive.")

    target_points = max(int(len(polygon) * (1 - percentage)), 3)

    if len(polygon) <= target_points:
        return polygon

    epsilon: float = 0
    approximated_points = polygon
    while len(approximated_points) > target_points:
        epsilon += epsilon_step
        candidate = np.squeeze(cv2.approxPolyDP(polygon, epsilon, closed=True), axis=1)
        # Stop before the approximation collapses below a valid polygon; keep the
        # last result with at least three points.
        if len(candidate) < 3:
            break
        approximated_points = candidate

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Express the fraction in [0, 1): use 0.5 for 'remove half the points', not 50.
  2. Clamp computed values: `percentage = min(max(percentage, 0.0), 0.999)`.
  3. Convert from a 0-100 scale: `percentage = pct_100 / 100.0`.

Example fix

# before
small = sv.approximate_polygon(polygon, percentage=30)  # meant 30%

# after
small = sv.approximate_polygon(polygon, percentage=0.3)
Defensive patterns

Strategy: validation

Validate before calling

if not 0 <= percentage < 1:
    percentage = min(max(percentage / 100.0 if percentage > 1 else percentage, 0.0), 0.999)
result = sv.approximate_polygon(polygon, percentage=percentage)

Type guard

def is_valid_percentage(v) -> bool:
    return isinstance(v, (int, float)) and 0 <= v < 1

Prevention

When it happens

Trigger: Calling `approximate_polygon(poly, percentage=50)` intending 50 percent (50 is far outside [0,1)); passing `percentage=1.0` to drop every point; computing the fraction dynamically and letting rounding push it to exactly 1.0 or below 0.

Common situations: Confusing the 0-1 fraction convention with a 0-100 scale; UI sliders that emit percentages 0-100 forwarded verbatim; derived values like `1 - keep_ratio` where keep_ratio rounds to 0.

Related errors


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