{"record":{"id":"27a96eb120c17491","repo":"roboflow/supervision","slug":"createml-annotation-refers-to-image-image-name-27a96e","errorCode":null,"errorMessage":"CreateML annotation refers to image {image_name}, which resolves to {resolved_image_path} — outside the images directory {images_directory_resolved}.","messagePattern":"CreateML annotation refers to image (.+?), which resolves to (.+?) — outside the images directory (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/createml.py","lineNumber":43,"sourceCode":"    resolved path so aliases collapse to a single dataset entry.\n    \"\"\"\n    images_directory_resolved = Path(images_directory_path).resolve()\n    image_path = Path(images_directory_path) / Path(image_name)\n    try:\n        resolved_image_path = image_path.resolve()\n    except (OSError, ValueError) as exc:\n        raise ValueError(\n            f\"CreateML annotation refers to image {image_name!r}, which \"\n            f\"produces an invalid path: {exc}\"\n        ) from exc\n    if resolved_image_path == images_directory_resolved:\n        raise ValueError(\n            f\"CreateML annotation refers to image {image_name!r}, which \"\n            f\"resolves to the images directory itself \"\n            f\"({images_directory_resolved}). Expected a path to an image file.\"\n        )\n    if images_directory_resolved not in resolved_image_path.parents:\n        raise ValueError(\n            f\"CreateML annotation refers to image {image_name!r}, which \"\n            f\"resolves to {resolved_image_path} — outside the images \"\n            f\"directory {images_directory_resolved}.\"\n        )\n    if resolved_image_path.is_dir():\n        raise ValueError(\n            f\"CreateML annotation refers to image {image_name!r}, which \"\n            f\"resolves to directory {resolved_image_path}. Expected a path \"\n            \"to an image file.\"\n        )\n    return str(resolved_image_path)\n\n\ndef createml_annotations_to_detections(\n    image_annotations: list[CreateMLDict], class_to_index: dict[str, int]\n) -> Detections:\n    \"\"\"Convert a single image's CreateML annotations into ``Detections``.\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/createml.py#L25-L61","documentation":"Raised by _resolve_image_path in the CreateML loader when the resolved image path is not under the resolved images directory. It rejects absolute image paths and '..' traversal, confining loads to images_directory_path exactly like the COCO loader does.","triggerScenarios":"A CreateML entry with \"image\": \"/abs/path/img.jpg\" or \"image\": \"../shared/img.jpg\". Note: joining an absolute path with Path(images_dir) / Path(abs) yields the absolute path, which then fails the parents containment check.","commonSituations":"Annotation files generated on a different machine or by a tool that writes absolute paths; datasets reorganized after annotation creation; pointing images_directory_path at the wrong folder.","solutions":["Rewrite image fields to bare relative filenames: os.path.basename(entry['image']).","Confirm images_directory_path is the directory that actually contains the referenced images.","Post-process the JSON once: for e in data: e['image'] = os.path.basename(e['image'])."],"exampleFix":"// before\n{\"image\": \"/mnt/data/train/img_0001.jpg\", ...}\n\n// after\n{\"image\": \"img_0001.jpg\", ...}","handlingStrategy":"validation","validationCode":"import json, os\nfrom pathlib import Path\n\ndef normalize_createml_images(annotations_path: str) -> None:\n    \"\"\"Rewrite absolute/traversal image values to bare filenames.\"\"\"\n    entries = json.loads(Path(annotations_path).read_text())\n    for e in entries:\n        if os.path.isabs(e[\"image\"]) or \"..\" in Path(e[\"image\"]).parts:\n            e[\"image\"] = os.path.basename(e[\"image\"])\n    Path(annotations_path).write_text(json.dumps(entries))","typeGuard":"def is_confined_createml_image(name: str, images_dir: str) -> bool:\n    \"\"\"True when the joined resolved path stays strictly inside images_dir.\"\"\"\n    root = Path(images_dir).resolve()\n    return root in (root / name).resolve().parents","tryCatchPattern":"try:\n    sv.DetectionDataset.from_createml(images_directory_path=d, annotations_path=a)\nexcept ValueError as exc:\n    if \"outside the images directory\" in str(exc):\n        normalize_createml_images(a)  # then retry the load\n    else:\n        raise","preventionTips":["Store bare relative filenames in the 'image' field.","Verify images_directory_path contains the referenced files before the first load.","Apply os.path.basename at generation time when consuming absolute paths."],"tags":["createml","dataset-load","path-traversal","security"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}