roboflow/supervision · error · ValueError
starting_image_id and starting_annotation_id must be >= 1 (C
Error message
starting_image_id and starting_annotation_id must be >= 1 (COCO spec requires 1-indexed ids); got starting_image_id={starting_image_id}, starting_annotation_id={starting_annotation_id} What it means
Raised by save_coco_annotations when starting_image_id or starting_annotation_id is less than 1. The COCO specification requires 1-indexed image and annotation ids, so supervision enforces this at the boundary of COCO export. These parameters exist so you can chain multiple splits (train/valid/test) without id collisions; the function returns the next unused ids for exactly that purpose.
Source
Thrown at src/supervision/dataset/formats/coco.py:665
Example:
```python
import supervision as sv
from supervision.dataset.formats.coco import save_coco_annotations
ds = sv.DetectionDataset.from_yolo(
images_directory_path="train/images",
annotations_directory_path="train/labels",
data_yaml_path="data.yaml",
)
next_img_id, next_ann_id = save_coco_annotations(
dataset=ds, annotation_path="out/train/annotations.json"
)
# next_img_id and next_ann_id are the first unused ids — pass them
# to the next split to keep ids globally unique across files.
```
"""
if starting_image_id < 1 or starting_annotation_id < 1:
raise ValueError(
"starting_image_id and starting_annotation_id must be >= 1 "
"(COCO spec requires 1-indexed ids); "
f"got {starting_image_id=}, {starting_annotation_id=}"
)
check_no_basename_collisions(
image_paths=dataset.image_paths,
key=lambda image_path: Path(image_path).name,
output_kind="COCO image",
)
Path(annotation_path).parent.mkdir(parents=True, exist_ok=True)
licenses = [
{
"id": 1,
"url": "https://creativecommons.org/licenses/by/4.0/",
"name": "CC BY 4.0",
}
]
View on GitHub (pinned to 7f254d9784)
Solutions
- Pass starting_image_id=1, starting_annotation_id=1 for the first split (or omit them — the defaults are 1).
- For subsequent splits, feed the values returned by the previous save_coco_annotations call into the next one, as shown in the function docstring.
- If computing ids yourself, assert ids >= 1 before calling the exporter.
Example fix
// before
next_img, next_ann = 0, 0
for split in splits:
next_img, next_ann = save_coco_annotations(ds, path, starting_image_id=next_img, starting_annotation_id=next_ann)
// after
next_img, next_ann = 1, 1 # COCO ids are 1-indexed
for split in splits:
next_img, next_ann = save_coco_annotations(ds, path, starting_image_id=next_img, starting_annotation_id=next_ann) Defensive patterns
Strategy: validation
Validate before calling
def next_coco_ids(start_img: int, start_ann: int) -> tuple[int, int]:
"""Validate 1-indexed COCO starting ids before export."""
if start_img < 1 or start_ann < 1:
raise ValueError(f"COCO ids must be >= 1, got {start_img=}, {start_ann=}")
return start_img, start_ann
next_img, next_ann = next_coco_ids(1, 1) # seed, then chain return values Type guard
def are_valid_coco_start_ids(img_id: int, ann_id: int) -> bool:
"""True when both starting ids satisfy COCO 1-indexing."""
return isinstance(img_id, int) and isinstance(ann_id, int) and img_id >= 1 and ann_id >= 1 Try / catch
try:
save_coco_annotations(dataset=ds, annotation_path=p, starting_image_id=i, starting_annotation_id=a)
except ValueError as exc:
if "1-indexed" in str(exc):
i, a = 1, 1 # reset to defaults and retry once
else:
raise Prevention
- Always seed the id counter at 1, never 0, when looping over splits.
- Propagate the (next_image_id, next_annotation_id) return values instead of computing ids by hand.
- Wrap split-export loops with an assertion that ids are monotonically increasing and >= 1.
When it happens
Trigger: Calling sv.dataset.formats.coco.save_coco_annotations (or the DetectionDataset.as_coco/save wrapper that forwards these kwargs) with starting_image_id=0, starting_annotation_id=0, or negative values. Typically happens when initializing a counter at 0 before a loop over splits, or when passing the length of a previous file instead of the returned next_id.
Common situations: Exporting a dataset split loop with enumerate(dataset_splits) starting at 0; porting code from a pipeline that used 0-based ids; ignoring the (next_image_id, next_annotation_id) return value and guessing ids manually.
Related errors
- Detection annotation for image {image_path} contains non-int
- Detection annotation for image {image_path} contains class_i
- Detections must include class_id for COCO export.
- COCO RLE counts must be one-dimensional.
- COCO RLE counts cannot be empty.
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/553577047a58cf88.
Report an issue: GitHub.