{"record":{"id":"80331785e1b3baa3","repo":"roboflow/supervision","slug":"border-sizes-must-be-non-negative","errorCode":null,"errorMessage":"Border sizes must be non-negative","messagePattern":"Border sizes must be non-negative","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/_cv2/_image.py","lineNumber":47,"sourceCode":"    else:\n        raise ValueError(f\"Unsupported flip code: {flip_code}\")\n    return np.ascontiguousarray(np.flip(image, axis=axes))\n\n\ndef _copy_make_border(\n    image: npt.NDArray[Any],\n    top: int,\n    bottom: int,\n    left: int,\n    right: int,\n    border_type: int,\n    value: int | float | Sequence[int | float] = 0,\n) -> npt.NDArray[Any]:\n    \"\"\"Add a constant border around an image.\"\"\"\n    if border_type != _BORDER_CONSTANT:\n        raise ValueError(\"Only BORDER_CONSTANT is supported by the fallback\")\n    if min(top, bottom, left, right) < 0:\n        raise ValueError(\"Border sizes must be non-negative\")\n\n    height, width = image.shape[:2]\n    shape = (height + top + bottom, width + left + right, *image.shape[2:])\n\n    # OpenCV's Scalar(v) fills only channel 0 and zero-pads the rest for\n    # multichannel images — a bare scalar is treated the same as a\n    # length-1 sequence, not broadcast to every channel.\n    sequence_value = value if isinstance(value, Sequence) else (value,)\n    values = np.asarray(sequence_value, dtype=image.dtype).reshape(-1)\n    if image.ndim == 2:\n        fill_value: Any = values[0] if values.size else 0\n    else:\n        fill = np.zeros(image.shape[2], dtype=image.dtype)\n        fill[: min(values.size, image.shape[2])] = values[: image.shape[2]]\n        fill_value = fill.reshape((1, 1, -1))\n\n    result = np.full(shape, fill_value, dtype=image.dtype)\n    result[top : top + height, left : left + width] = image","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_image.py#L29-L65","documentation":"`cv2.copyMakeBorder` fallback at src/supervision/_cv2/_image.py:47 requires all four border sizes (top, bottom, left, right) to be >= 0. Negative borders would mean cropping, which the function's output-shape construction `(H + top + bottom, W + left + right, ...)` cannot represent, so they are rejected before array allocation.","triggerScenarios":"Passing a negative border width, usually from arithmetic on computed pad sizes: e.g. `pad = (target - size) // 2` going negative when the image is already larger than the target, then forwarded to copyMakeBorder.","commonSituations":"Letterboxing/resizing helpers that compute symmetric pads without clamping; config-driven pad values where a minus sign typo survives into runtime.","solutions":["Clamp computed pads to zero: `top = max(0, (target_h - h) // 2)`","If negative pad means crop, do the crop explicitly instead: `img[-top:h+bottom, -left:w+right]`","Validate pad configuration at load time and reject negative values early"],"exampleFix":"// before\npad_top = (target_h - h) // 2  # negative when h > target_h\nout = cv2.copyMakeBorder(img, pad_top, pad_top, 0, 0, cv2.BORDER_CONSTANT)\n\n// after\npad_top = max(0, (target_h - h) // 2)\nout = cv2.copyMakeBorder(img, pad_top, pad_top, 0, 0, cv2.BORDER_CONSTANT)","handlingStrategy":"validation","validationCode":"def clamp_pads(top: int, bottom: int, left: int, right: int) -> tuple[int, int, int, int]:\n    \"\"\"copyMakeBorder requires non-negative border sizes.\"\"\"\n    pads = tuple(max(0, int(p)) for p in (top, bottom, left, right))\n    if min(top, bottom, left, right) < 0:\n        # negative pad usually means the image already exceeds the target — crop instead\n        pass\n    return pads  # type: ignore[return-value]","typeGuard":"def is_non_negative_pad(*sizes: int) -> bool:\n    \"\"\"All four border sizes must be >= 0.\"\"\"\n    return all(isinstance(s, int) and s >= 0 for s in sizes)","tryCatchPattern":null,"preventionTips":["Clamp letterbox pad math with max(0, ...) at the computation site","Treat negative computed pads as a signal to crop, not pad"],"tags":["cv2-fallback","padding","validation","arithmetic-bug"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}