{"record":{"id":"6b7c1709fc211450","repo":"roboflow/supervision","slug":"expected-covariance-shape-expected-shape-got-c","errorCode":null,"errorMessage":"Expected covariance shape {expected_shape}, got {covariances_array.shape}.","messagePattern":"Expected covariance shape (.+?), got (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/key_points/annotators.py","lineNumber":322,"sourceCode":"        sorted_indices = sorted(\n            range(len(sigma_seq)), key=lambda i: sigma_seq[i], reverse=True\n        )\n        self.sigma = [sigma_seq[i] for i in sorted_indices]\n        self.color = [color_seq[i] for i in sorted_indices]\n        self.max_axis = max_axis\n\n    def _get_covariances(self, key_points: KeyPoints) -> npt.NDArray[np.float32]:\n        covariances = key_points.data.get(\"covariance\")\n        if covariances is None:\n            raise ValueError(\n                \"key_points.data must contain 'covariance' with shape (N, K, 2, 2).\"\n            )\n        covariances_array = cast(\n            npt.NDArray[np.float32], np.asarray(covariances, dtype=np.float32)\n        )\n        expected_shape = (*key_points.xy.shape[:2], 2, 2)\n        if covariances_array.shape != expected_shape:\n            raise ValueError(\n                f\"Expected covariance shape {expected_shape}, \"\n                f\"got {covariances_array.shape}.\"\n            )\n        return covariances_array\n\n    def _decompose_covariance(\n        self, covariance: npt.NDArray[np.float32]\n    ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]] | None:\n        \"\"\"Eigendecompose a 2x2 covariance, returning sorted (eigenvalues, vectors).\"\"\"\n        if not np.isfinite(covariance).all():\n            return None\n        try:\n            eigenvalues, eigenvectors = np.linalg.eigh(covariance.astype(np.float64))\n        except np.linalg.LinAlgError:\n            return None\n        if not np.isfinite(eigenvalues).all() or np.any(eigenvalues <= 0):\n            return None\n        order = np.argsort(eigenvalues)[::-1]","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/key_points/annotators.py#L304-L340","documentation":"Raised by ColorPalette.by_idx when the palette's colors list is empty, before any indexing happens. A ColorPalette with zero colors cannot serve a lookup, so the method refuses rather than returning None or wrapping an empty list. Any by_idx call — including by_idx(0) — on an empty palette triggers this.","triggerScenarios":"Constructing sv.ColorPalette(colors=[]) or sv.ColorPalette.from_hex([]) then calling .by_idx(i); building palettes from a dynamically generated hex list that is empty on the first iteration; deserializing a palette from config where the colors key is an empty list.","commonSituations":"Palette built from per-class colors loaded from user config that is missing/empty; loop-driven palette construction where the source list is empty for some class set; downstream of from_matplotlib with color_count < 1 being caught and defaulting to an empty palette instead.","solutions":["Ensure the colors list passed to sv.ColorPalette(...) or from_hex([...]) is non-empty.","Default to the built-in palette when your dynamic list is empty: palette = sv.ColorPalette.from_hex(hexes) if hexes else sv.ColorPalette.DEFAULT.","Add an early check in config-loading code that rejects or warns on empty color lists instead of constructing the palette.","If you only need cyclic colors over unknown class counts, construct a palette of a fixed size once and rely on by_idx's modulo wrapping."],"exampleFix":"# before\npalette = sv.ColorPalette.from_hex([])  # later palette.by_idx(0) raises\n\n# after\npalette = sv.ColorPalette.from_hex(hexes) if hexes else sv.ColorPalette.DEFAULT\n# or simply: palette = sv.ColorPalette.from_hex(hexes or ['#FF0000', '#00FF00', '#0000FF'])","handlingStrategy":"validation","validationCode":"if not hex_colors:\n    raise ValueError(f\"color list is empty; cannot build palette: {hex_colors!r}\")\npalette = sv.ColorPalette.from_hex(hex_colors)\n\n# or fall back to the default palette:\n# palette = sv.ColorPalette.from_hex(hex_colors or ['#FF0000', '#00FF00', '#0000FF'])","typeGuard":"def has_colors(palette: sv.ColorPalette) -> bool:\n    \"\"\"True if the palette can serve by_idx lookups.\"\"\"\n    return len(palette.colors) > 0","tryCatchPattern":null,"preventionTips":["Check len(colors) > 0 before constructing ColorPalette from dynamic lists.","Validate color config at load time (non-empty, valid hexes) so failures surface at startup, not mid-annotation.","Keep a sensible default palette constant to fall back to when the dynamic list is empty."],"tags":["validation","color-palette","empty-list","valueerror"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}