{"record":{"id":"553577047a58cf88","repo":"roboflow/supervision","slug":"starting-image-id-and-starting-annotation-id-must","errorCode":null,"errorMessage":"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}","messagePattern":"starting_image_id and starting_annotation_id must be >= 1 \\(COCO spec requires 1-indexed ids\\); got starting_image_id=(.+?), starting_annotation_id=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/coco.py","lineNumber":665,"sourceCode":"    Example:\n        ```python\n        import supervision as sv\n        from supervision.dataset.formats.coco import save_coco_annotations\n\n        ds = sv.DetectionDataset.from_yolo(\n            images_directory_path=\"train/images\",\n            annotations_directory_path=\"train/labels\",\n            data_yaml_path=\"data.yaml\",\n        )\n        next_img_id, next_ann_id = save_coco_annotations(\n            dataset=ds, annotation_path=\"out/train/annotations.json\"\n        )\n        # next_img_id and next_ann_id are the first unused ids — pass them\n        # to the next split to keep ids globally unique across files.\n        ```\n    \"\"\"\n    if starting_image_id < 1 or starting_annotation_id < 1:\n        raise ValueError(\n            \"starting_image_id and starting_annotation_id must be >= 1 \"\n            \"(COCO spec requires 1-indexed ids); \"\n            f\"got {starting_image_id=}, {starting_annotation_id=}\"\n        )\n    check_no_basename_collisions(\n        image_paths=dataset.image_paths,\n        key=lambda image_path: Path(image_path).name,\n        output_kind=\"COCO image\",\n    )\n    Path(annotation_path).parent.mkdir(parents=True, exist_ok=True)\n    licenses = [\n        {\n            \"id\": 1,\n            \"url\": \"https://creativecommons.org/licenses/by/4.0/\",\n            \"name\": \"CC BY 4.0\",\n        }\n    ]\n","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/coco.py#L647-L683","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nnext_img, next_ann = 0, 0\nfor split in splits:\n    next_img, next_ann = save_coco_annotations(ds, path, starting_image_id=next_img, starting_annotation_id=next_ann)\n\n// after\nnext_img, next_ann = 1, 1  # COCO ids are 1-indexed\nfor split in splits:\n    next_img, next_ann = save_coco_annotations(ds, path, starting_image_id=next_img, starting_annotation_id=next_ann)","handlingStrategy":"validation","validationCode":"def next_coco_ids(start_img: int, start_ann: int) -> tuple[int, int]:\n    \"\"\"Validate 1-indexed COCO starting ids before export.\"\"\"\n    if start_img < 1 or start_ann < 1:\n        raise ValueError(f\"COCO ids must be >= 1, got {start_img=}, {start_ann=}\")\n    return start_img, start_ann\n\nnext_img, next_ann = next_coco_ids(1, 1)  # seed, then chain return values","typeGuard":"def are_valid_coco_start_ids(img_id: int, ann_id: int) -> bool:\n    \"\"\"True when both starting ids satisfy COCO 1-indexing.\"\"\"\n    return isinstance(img_id, int) and isinstance(ann_id, int) and img_id >= 1 and ann_id >= 1","tryCatchPattern":"try:\n    save_coco_annotations(dataset=ds, annotation_path=p, starting_image_id=i, starting_annotation_id=a)\nexcept ValueError as exc:\n    if \"1-indexed\" in str(exc):\n        i, a = 1, 1  # reset to defaults and retry once\n    else:\n        raise","preventionTips":["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."],"tags":["coco","dataset-export","validation","argument-validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}