{"record":{"id":"4609b1dff7e0afd6","repo":"roboflow/supervision","slug":"epsilon-step-must-be-positive","errorCode":null,"errorMessage":"epsilon_step must be positive.","messagePattern":"epsilon_step must be positive\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/detection/utils/polygons.py","lineNumber":114,"sourceCode":"        >>> result = approximate_polygon(polygon, percentage=0.5)\n        >>> result.shape[1]\n        2\n        >>> len(result) <= max(int(len(polygon) * 0.5), 3)\n        True\n\n        Polygon already at or below target — returned unchanged:\n\n        >>> tiny = np.array([[0, 0], [5, 0], [2, 4]])\n        >>> approximate_polygon(tiny, percentage=0.5) is tiny\n        True\n\n        ```\n    \"\"\"\n\n    if percentage < 0 or percentage >= 1:\n        raise ValueError(\"Percentage must be in the range [0, 1).\")\n    if epsilon_step <= 0:\n        raise ValueError(\"epsilon_step must be positive.\")\n\n    target_points = max(int(len(polygon) * (1 - percentage)), 3)\n\n    if len(polygon) <= target_points:\n        return polygon\n\n    epsilon: float = 0\n    approximated_points = polygon\n    while len(approximated_points) > target_points:\n        epsilon += epsilon_step\n        candidate = np.squeeze(cv2.approxPolyDP(polygon, epsilon, closed=True), axis=1)\n        # Stop before the approximation collapses below a valid polygon; keep the\n        # last result with at least three points.\n        if len(candidate) < 3:\n            break\n        approximated_points = candidate\n\n    return approximated_points","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/detection/utils/polygons.py#L96-L132","documentation":"Raised by approximate_polygon when the epsilon_step argument is zero or negative. epsilon_step controls how much the Douglas-Peucker tolerance grows on each iteration of the loop that simplifies the polygon toward the target point count. A non-positive step would make no progress (infinite loop) or move backward, so it is rejected up front.","triggerScenarios":"Calling supervision.detection.utils.polygons.approximate_polygon(polygon, percentage=0.5, epsilon_step=0) or with a negative epsilon_step. Also reachable via any pipeline that computes epsilon_step dynamically (e.g. proportional to box area) and lets it round down to 0 for tiny polygons.","commonSituations":"Passing 0 expecting 'no simplification' semantics; deriving epsilon_step from image scale so that very small regions produce 0; copy-pasting a default of 0 from another API where 0 means 'auto'.","solutions":["Pass a small positive step such as epsilon_step=0.1 (the loop increments by this value each iteration).","If the step is computed dynamically, clamp it: epsilon_step = max(epsilon_step, 0.1).","If you do not want simplification at all, skip the call or pass percentage=0 (with a valid positive step) — the function already returns the polygon unchanged when it is at or below the target point count."],"exampleFix":"// before\napproximate_polygon(poly, percentage=0.5, epsilon_step=0)\n\n// after\napproximate_polygon(poly, percentage=0.5, epsilon_step=0.1)","handlingStrategy":"validation","validationCode":"epsilon_step = max(float(epsilon_step), 0.1)\nresult = approximate_polygon(polygon, percentage=0.5, epsilon_step=epsilon_step)","typeGuard":"def is_valid_epsilon_step(v: float) -> bool:\n    return float(v) > 0","tryCatchPattern":"try:\n    simplified = approximate_polygon(poly, percentage=p, epsilon_step=e)\nexcept ValueError as err:\n    if 'epsilon_step' in str(err):\n        simplified = approximate_polygon(poly, percentage=p, epsilon_step=0.1)\n    else:\n        raise","preventionTips":["Never use 0 to mean 'no simplification' — percentage=0 with a positive step already returns small polygons unchanged.","When deriving epsilon_step from image scale, clamp with max(step, 0.1)."],"tags":["polygons","validation","douglas-peucker","valueerror"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}