roboflow/supervision · error · ValueError
epsilon must be non-negative
Error message
epsilon must be non-negative
What it means
cv2.approxPolyDP's epsilon is a distance tolerance in pixels; negative values are meaningless and OpenCV's own implementation requires epsilon >= 0. The fallback validates this up front before running its Ramer-Douglas-Pecker-style simplification.
Source
Thrown at src/supervision/_cv2/_geometry.py:167
continue
destination[write_position] = point
start_point = point
write_position = (write_position + 1) % count
point = end_point
index += 1
if not closed:
destination[write_position] = point
return np.asarray(destination[:new_count], dtype=np.float64)
def _approx_poly_dp(
contour: npt.NDArray[Any], epsilon: float, closed: bool
) -> npt.NDArray[Any]:
"""Approximate a contour with the supported OpenCV polygon contract."""
if epsilon < 0:
raise ValueError("epsilon must be non-negative")
points = _as_points(contour)
if len(points) == 0:
dtype = np.asarray(contour).dtype
return np.empty((0, 1, 2), dtype=dtype)
epsilon_squared = float(epsilon) ** 2
simplified = _simplify_slices(points, epsilon_squared, closed)
simplified = _cleanup_approximation(simplified, epsilon_squared, closed)
dtype = np.asarray(contour).dtype
return simplified.astype(dtype, copy=False).reshape(-1, 1, 2)
def _cross(edge: npt.NDArray[np.float64], point: npt.NDArray[np.float64]) -> float:
"""Return the two-dimensional cross product of two vectors."""
return float(edge[0] * point[1] - edge[1] * point[0])
View on GitHub (pinned to 7f254d9784)
Solutions
- Pass a non-negative epsilon; the common idiom is epsilon = 0.02 * cv2.arcLength(contour, True).
- Resolve 'unset' sentinels to a computed default before calling.
- Clamp or validate config-supplied epsilon: if epsilon < 0: raise/config error early.
Example fix
# before epsilon = -0.03 * cv2.arcLength(contour, True) # sign typo approx = cv2.approxPolyDP(contour, epsilon, True) # after epsilon = 0.03 * cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, epsilon, True)
Defensive patterns
Strategy: validation
Validate before calling
epsilon = max(0.0, 0.02 * cv2.arcLength(contour, True)) approx = cv2.approxPolyDP(contour, epsilon, True)
Prevention
- Compute epsilon from arcLength with a positive ratio
- Resolve -1 'unset' sentinels before the call
- Validate epsilon >= 0 when it comes from config
When it happens
Trigger: Passing a negative epsilon, typically from a computed value like -0.02 * perimeter due to a sign error, or from a config default set to -1 as an 'unset' sentinel.
Common situations: Sentinel patterns (epsilon = -1 meaning 'auto') that are never replaced before the call; arithmetic bugs producing negative tolerances; copying formulas with a typo'd minus sign.
Related errors
- Resize dimensions must be positive
- Unsupported flip code: {flip_code}
- Blur kernel dimensions must be positive
- Connected-component input must be a two-dimensional image
- Only 4- and 8-connectivity are supported
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/be83756d42fd2538.
Report an issue: GitHub.