{"record":{"id":"9fd6ae5933af03ed","repo":"roboflow/supervision","slug":"mask-must-contain-n-masks-but-got-len-mask","errorCode":null,"errorMessage":"mask must contain {n} masks, but got {len(mask)}","messagePattern":"mask must contain (.+?) masks, but got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/validators/__init__.py","lineNumber":45,"sourceCode":"\n@deprecated(  # type: ignore[untyped-decorator]\n    target=_validate_xyxy,\n    deprecated_in=\"0.29.0\",\n    remove_in=\"0.32.0\",\n)\ndef validate_xyxy(xyxy: Any) -> None:\n    void(xyxy)\n\n\ndef _validate_mask(mask: Any, n: int) -> None:\n    if mask is None:\n        return\n\n    # Fast path: CompactMask only needs a length check.\n\n    if isinstance(mask, CompactMask):\n        if len(mask) != n:\n            raise ValueError(f\"mask must contain {n} masks, but got {len(mask)}\")\n        return\n\n    expected_shape = f\"({n}, H, W)\"\n    actual_shape = str(getattr(mask, \"shape\", None))\n    actual_dtype = getattr(mask, \"dtype\", None)\n\n    is_valid_shape = (\n        isinstance(mask, np.ndarray) and len(mask.shape) == 3 and mask.shape[0] == n\n    )\n    if not is_valid_shape:\n        raise ValueError(\n            \"mask must be a 3D np.ndarray with shape \"\n            + f\"{expected_shape}, but got shape {actual_shape}\"\n        )\n    if not np.issubdtype(actual_dtype, bool):\n        warn_deprecated(\n            f\"A `Detections` object was created with a mask of type {actual_dtype}.\"\n            \" Masks of type other than `bool` are deprecated and may produce unexpected\"","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/validators/__init__.py#L27-L63","documentation":"Raised by supervision.validators._validate_mask when the mask is a CompactMask whose entry count differs from n, the number of rows in xyxy. Each detection needs exactly one mask; with CompactMask the count check is a simple len() comparison before shape checks apply to the decompressed form.","triggerScenarios":"Constructing Detections(xyxy=boxes, mask=CompactMask(...)) where len(mask) != len(boxes); e.g. 10 boxes with 9 encoded masks after filtering boxes but not masks.","commonSituations":"Applying confidence/class filters to xyxy but forgetting the mask; combining boxes and masks produced at different pipeline stages (NMS applied to one, not the other); serializing/deserializing CompactMask and losing an entry.","solutions":["Apply the same filter mask to both: det = det[keep_idx] via Detections.__getitem__, which keeps xyxy and mask aligned.","Rebuild the CompactMask from the filtered mask array after selection.","Assert len(mask) == len(xyxy) before constructing Detections."],"exampleFix":"# before\ndets = Detections(xyxy=boxes, mask=compact_mask)  # 10 boxes, 9 masks\n\n# after\nkeep = confidence > 0.5\ndets = Detections(xyxy=boxes[keep], mask=CompactMask.from_mask(masks_arr[keep]))","handlingStrategy":"validation","validationCode":"assert len(compact_mask) == len(xyxy), (\n    f\"mask count {len(compact_mask)} != box count {len(xyxy)}\"\n)\ndets = Detections(xyxy=xyxy, mask=compact_mask)","typeGuard":"def mask_matches_boxes(mask: CompactMask, xyxy: np.ndarray) -> bool:\n    return len(mask) == len(xyxy)","tryCatchPattern":null,"preventionTips":["Filter detections with det[idx] so masks stay aligned.","Rebuild CompactMask after any box filtering.","Prefer building Detections once from full model output, then filter the object."],"tags":["detections","mask","compact-mask","validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}