{"record":{"id":"ec338ebb2d484df4","repo":"roboflow/supervision","slug":"contour-border-tracing-did-not-converge","errorCode":null,"errorMessage":"Contour border tracing did not converge","messagePattern":"Contour border tracing did not converge","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"src/supervision/_cv2/_contours.py","lineNumber":85,"sourceCode":"            if is_foreground(row, column):\n                next_point = (row, column)\n                break\n            if candidate_direction == 0:\n                east_zero = True\n\n        if east_zero:\n            labels[current] = -border_number\n        elif labels[current] == 0:\n            labels[current] = border_number\n        contour.append(current)\n\n        if next_point == start and current == first_neighbor and len(contour) > 1:\n            return contour\n        if next_point is None:\n            return contour\n        previous_point, current = current, next_point\n        if len(contour) > 4 * image.size:\n            raise RuntimeError(\"Contour border tracing did not converge\")\n\n\ndef _trace_borders(mask: npt.NDArray[np.bool_]) -> list[np.ndarray]:\n    \"\"\"Trace all foreground and hole borders in raster candidate order.\"\"\"\n    image = np.ascontiguousarray(mask, dtype=bool)\n    labels = np.zeros(image.shape, dtype=np.int32)\n    left_zero = image & ~np.pad(image[:, :-1], ((0, 0), (1, 0)))\n    right_zero = image & ~np.pad(image[:, 1:], ((0, 0), (0, 1)))\n    candidates = np.argwhere(left_zero | right_zero)\n    borders: list[np.ndarray] = []\n    border_number = 1\n    for row, column in candidates:\n        row, column = int(row), int(column)\n        if left_zero[row, column] and labels[row, column] == 0:\n            border_number += 1\n            border = _follow_border(\n                image, labels, (row, column), (row, column - 1), border_number\n            )","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_contours.py#L67-L103","documentation":"The fallback contour tracer implements Moore-neighbor/Suzuki-style border following; each border walk is bounded by `4 * image.size` steps at src/supervision/_cv2/_contours.py:85. If the walk has not returned to its start point by then, the trace is considered non-convergent and a RuntimeError is raised — this is an internal invariant, not a user-input error, and indicates either a bug in the tracer or memory corruption of the label array.","triggerScenarios":"Essentially unreachable through normal API use; conceivably triggered by pathological masks (e.g. extremely complex checkerboards), a corrupted boolean mask (non-contiguous views mutated concurrently), or a bug in the neighbor-selection logic for a specific border configuration.","commonSituations":"Long-running processes with threaded mutation of shared numpy arrays; unusual synthetic masks stress-testing the fallback backend; a genuine supervision bug — check the issue tracker with a reproducing mask.","solutions":["Capture the input mask when the error occurs and reduce it to a minimal reproducer","Install `opencv-python` so the C++ Suzuki-Abe implementation is used instead of the fallback","Report the reproducer to the supervision maintainers (internal invariant violation)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\nexcept RuntimeError as err:\n    if \"did not converge\" in str(err):\n        np.save(\"/tmp/nonconvergent_mask.npy\", mask)  # preserve the reproducer\n        raise RuntimeError(f\"contour tracer failed on mask {mask.shape}; saved for bug report\") from err\n    raise","preventionTips":["Treat this error as a bug report trigger — save the input mask and report it upstream","Install opencv-python in production to use the battle-tested C++ tracer","Avoid sharing/mutating the mask array across threads while tracing"],"tags":["cv2-fallback","contours","internal-invariant","runtime-error"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}