Skip to content

Troubleshooting

Common issues and how to fix them.

Import errors

ModuleNotFoundError: No module named 'hotcoco'

The package isn't installed in the active environment. Run:

pip install hotcoco

If you're in a virtual environment, make sure it's activated before installing.


ImportError after upgrading numpy

hotcoco ships prebuilt wheels that bundle a compiled Rust extension. If you upgrade numpy to a major version that changes the C ABI (numpy 1.x → 2.x, for example), you might need to reinstall hotcoco to pick up a compatible wheel:

pip install --upgrade hotcoco

The coco CLI distinguishes this case: when the package is present but its compiled extension fails to import (broken wheel, wrong platform), the error says so and suggests reinstalling rather than claiming hotcoco is not installed.


Name conflict with pycocotools

If you have both hotcoco and pycocotools installed and import COCO from pycocotools by accident:

# Wrong — gets pycocotools
from pycocotools.coco import COCO

# Right — gets hotcoco
from hotcoco import COCO

To use hotcoco as a drop-in without changing any imports, call init_as_pycocotools() once at the start of your script — see Framework integrations for the details and per-framework notes.


Detection format errors

load_res raises a KeyError or returns empty results

Your detection file is missing a required field. The fields each iou_type needs, with a minimal example, are in Bounding box evaluation and the segmentation and keypoint sections of the same page; if the boxes load but the numbers look wrong, check Bbox format.


Bbox format: [x1, y1, x2, y2] vs [x, y, w, h]

COCO uses [x, y, width, height] (top-left corner + size) in pixel coordinates. Two common pitfalls:

  • Many model outputs use [x1, y1, x2, y2] (two corners) instead of [x, y, w, h]
  • Some formats, such as YOLO, use normalized coordinates in [0, 1] — COCO always expects pixel values

Convert before passing to load_res:

# Convert XYXY → XYWH
detections = [
    {**d, "bbox": [d["bbox"][0], d["bbox"][1],
                   d["bbox"][2] - d["bbox"][0],
                   d["bbox"][3] - d["bbox"][1]]}
    for d in raw_detections
]

image_id not found in ground truth

If a detection's image_id doesn't exist in the ground-truth dataset, it won't raise an error — load_res accepts it, but the evaluator silently ignores it since there's no matching GT image to evaluate against. No warning is emitted, so mismatches can be hard to spot.

Verify your image IDs match:

gt_img_ids = set(coco_gt.get_img_ids())
dt_img_ids = {d["image_id"] for d in detections}
missing = dt_img_ids - gt_img_ids
if missing:
    print(f"Detections reference {len(missing)} unknown image IDs: {list(missing)[:5]} ...")

Segmentation issues

RLE counts field: bytes vs string

mask.encode returns counts as bytes, and JSON cannot hold bytes — the conversion is in RLE counts is bytes, not a string.


size field order: [height, width]

COCO RLE uses [height, width] order, not [width, height]. If your masks look wrong or you get shape mismatches, check that size matches the image dimensions in [H, W] order.


Evaluation results

Metrics differ slightly from pycocotools

First check that ev.params matches the configuration you expect — mismatched iou_thrs or area_rng is the usual cause. With default parameters, hotcoco matches pycocotools to the last bits of a float64 (see Benchmarks); anything above about 1e-12 is a bug worth reporting.


summarize() prints -1.000

Every metric is -1.000. evaluate() found no matching (image_id, category_id) pairs between GT and DT. Common causes:

  • Wrong iou_type — for example, passing segmentation results to COCOeval(..., "bbox")
  • category_id mismatch — model uses 0-indexed classes but COCO uses 1-indexed IDs
  • All detections were dropped by load_res — see image_id not found in ground truth

Check that categories align:

gt_cats = {c["id"]: c["name"] for c in coco_gt.load_cats(coco_gt.get_cat_ids())}
dt_cat_ids = {d["category_id"] for d in detections}
missing_cats = dt_cat_ids - set(gt_cats)
if missing_cats:
    print(f"Unknown category IDs in detections: {missing_cats}")

Some metrics are -1.000. You customized iou_thrs, max_dets, or area_rng, and the fixed 12-line display has no value for that slot — AP50 needs 0.50 in ev.params.iou_thrs, for example. This is expected; ev.get_results() returns only the metrics that were computed. See summarize.


tide_errors() raises RuntimeError

tide_errors() needs evaluate() to have run first — see tide_errors.


Getting help

If your issue isn't covered here, open an issue on GitHub with a minimal reproducer.