{"record":{"id":"800d431dc934f6b0","repo":"PaddlePaddle/PaddleOCR","slug":"path-missing-items-array","errorCode":null,"errorMessage":"{path}: missing 'items' array","messagePattern":"(.+?): missing 'items' array","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deploy/ios_demo/scripts/compare_ocr_json.py","lineNumber":97,"sourceCode":"        return Polygon(pts)\n\n    p1 = _to_poly(poly_a)\n    p2 = _to_poly(poly_b)\n    if p1.is_empty or p2.is_empty or not p1.is_valid or not p2.is_valid:\n        return 0.0\n    inter = p1.intersection(p2).area\n    union = p1.union(p2).area\n    if union <= 0:\n        return 0.0\n    return float(inter / union)\n\n\ndef _load_items(path: Path) -> List[Dict[str, Any]]:\n    with path.open(\"r\", encoding=\"utf-8\") as f:\n        data = json.load(f)\n    items = data.get(\"items\")\n    if not isinstance(items, list):\n        raise ValueError(f\"{path}: missing 'items' array\")\n    return items\n\n\ndef _greedy_match(\n    ref_items: List[Dict[str, Any]],\n    hyp_items: List[Dict[str, Any]],\n    iou_threshold: float,\n) -> Tuple[List[Tuple[int, int, float]], List[int], List[int]]:\n    \"\"\"Return (pairs as ref_idx, hyp_idx, iou), unmatched_ref, unmatched_hyp.\"\"\"\n    candidates: List[Tuple[float, int, int]] = []\n    for i, ri in enumerate(ref_items):\n        ra = ri.get(\"polygon\")\n        if not isinstance(ra, list):\n            continue\n        for j, hj in enumerate(hyp_items):\n            ha = hj.get(\"polygon\")\n            if not isinstance(ha, list):\n                continue","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/deploy/ios_demo/scripts/compare_ocr_json.py#L79-L115","documentation":"Raised by _load_items in compare_ocr_json.py when a JSON file parses successfully but has no top-level \"items\" key holding a list. The comparison protocol expects the schema {\"items\": [{\"polygon\": ..., \"text\": ...}, ...]}; anything else (different key name, items as an object, wrong file) fails this validation.","triggerScenarios":"Passing a raw detection output file, an export from a different tool, or a JSON where the array is stored under another key (e.g. \"results\", \"regions\") or nested one level deeper.","commonSituations":"Comparing reference annotations against iOS demo output when one side was regenerated by a newer exporter with a changed schema; feeding an arbitrary OCR JSON downloaded or hand-written in a different shape; empty JSON object {} from a failed upstream export.","solutions":["Rewrite both input files to the expected schema: a top-level object whose \"items\" value is an array of {\"polygon\", \"text\"} entries.","If the data lives under a different key, rename it to \"items\" (e.g. with a small jq/python transform).","Open the file and verify with python -c \"import json;print(type(json.load(open('f.json')).get('items')))\" before comparing."],"exampleFix":"# before\n{\"results\": [{\"polygon\": [[0,0],[1,0],[1,1],[0,1]], \"text\": \"hi\"}]}\n\n# after\n{\"items\": [{\"polygon\": [[0,0],[1,0],[1,1],[0,1]], \"text\": \"hi\"}]}","handlingStrategy":"validation","validationCode":"import json\n\ndef load_items_checked(path: str) -> list:\n    with open(path, encoding=\"utf-8\") as f:\n        data = json.load(f)\n    items = data.get(\"items\")\n    assert isinstance(items, list) and items, f\"{path}: expected non-empty 'items' array\"\n    return items\n\nload_items_checked(ref_path); load_items_checked(hyp_path)","typeGuard":"def has_items_schema(data) -> bool:\n    return (\n        isinstance(data, dict)\n        and isinstance(data.get(\"items\"), list)\n        and all(isinstance(it, dict) for it in data[\"items\"])\n    )","tryCatchPattern":"try:\n    items = _load_items(path)\nexcept ValueError as e:\n    if \"missing 'items'\" in str(e):\n        raise ValueError(f\"{path}: wrong schema — expected {{\\\"items\\\": [...]}}\") from e\n    raise","preventionTips":["Validate both input files against the schema before running comparisons.","Write a tiny schema check into CI for any regenerated OCR JSON exports.","Keep exporters and the compare script in sync when the format changes."],"tags":["paddleocr","ios","json","schema","ocr-eval"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}