roboflow/supervision · error · ValueError

Only None hierarchy is supported by the fallback

Error message

Only None hierarchy is supported by the fallback

What it means

OpenCV's `drawContours` walks an optional hierarchy tree to also draw nested descendant contours. The Pillow fallback in src/supervision/_cv2/_drawing.py:270 only implements flat `contourIdx` selection, so it rejects any non-None hierarchy (and ignores `maxLevel`, matching OpenCV's behavior when hierarchy is None) instead of drawing a subset different from what was asked.

Source

Thrown at src/supervision/_cv2/_drawing.py:270

def _draw_contours(
    image: _ImageArray,
    contours: Sequence[npt.NDArray[Any]],
    contourIdx: int,
    color: Any,
    thickness: int = 1,
    lineType: int = 8,
    hierarchy: npt.NDArray[Any] | None = None,
    maxLevel: int = 2**31 - 1,
    offset: tuple[int, int] = (0, 0),
) -> _ImageArray:
    """Draw selected contours with OpenCV-compatible in-place mutation."""
    del lineType, maxLevel
    # OpenCV walks the hierarchy tree to also draw a contour's nested descendants;
    # the fallback only does flat contourIdx selection, so reject a hierarchy
    # instead of silently diverging. maxLevel is a no-op without a hierarchy, which
    # matches OpenCV ignoring it whenever hierarchy is None.
    if hierarchy is not None:
        raise ValueError("Only None hierarchy is supported by the fallback")
    selected = contours if contourIdx < 0 else contours[contourIdx : contourIdx + 1]

    def draw_selected(draw: Any) -> None:
        for contour in selected:
            points = _points(contour, offset=offset)
            if len(points) < 2:
                continue
            if thickness < 0:
                draw.polygon(points, fill=1)
            else:
                width = max(1, thickness)
                draw.line([*points, points[0]], fill=1, width=width, joint="curve")

    return _paint(image, _drawing_mask(image, draw_selected), color)

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Pass `hierarchy=None` and select contours yourself via `contourIdx` or by slicing the list
  2. Draw each contour individually in a loop instead of relying on hierarchy traversal
  3. Install `opencv-python` for full hierarchy-aware drawing

Example fix

// before
contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, color, 2, hierarchy=hierarchy)

// after
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    cv2.drawContours(img, contours, i, color, 2, hierarchy=None)
Defensive patterns

Strategy: validation

Validate before calling

def draw_all_contours(img, contours, color, thickness):
    """Draw contours without relying on hierarchy traversal."""
    for i in range(len(contours)):
        cv2.drawContours(img, contours, i, color, thickness, hierarchy=None)
    return img

Prevention

When it happens

Trigger: Calling `cv2.drawContours` with a `hierarchy` array returned from `cv2.findContours` while opencv-python is not installed — the standard OpenCV idiom of passing the full findContours result (contours, hierarchy, contourIdx=-1) to draw everything.

Common situations: Ported OpenCV visualization code that always forwards hierarchy; trying to draw only outer or only hole contours via hierarchy traversal in a fallback-only environment.

Related errors


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