PaddlePaddle/PaddleOCR · error · ValueError
{path}: missing 'items' array
Error message
{path}: missing 'items' array What it means
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.
Source
Thrown at deploy/ios_demo/scripts/compare_ocr_json.py:97
return Polygon(pts)
p1 = _to_poly(poly_a)
p2 = _to_poly(poly_b)
if p1.is_empty or p2.is_empty or not p1.is_valid or not p2.is_valid:
return 0.0
inter = p1.intersection(p2).area
union = p1.union(p2).area
if union <= 0:
return 0.0
return float(inter / union)
def _load_items(path: Path) -> List[Dict[str, Any]]:
with path.open("r", encoding="utf-8") as f:
data = json.load(f)
items = data.get("items")
if not isinstance(items, list):
raise ValueError(f"{path}: missing 'items' array")
return items
def _greedy_match(
ref_items: List[Dict[str, Any]],
hyp_items: List[Dict[str, Any]],
iou_threshold: float,
) -> Tuple[List[Tuple[int, int, float]], List[int], List[int]]:
"""Return (pairs as ref_idx, hyp_idx, iou), unmatched_ref, unmatched_hyp."""
candidates: List[Tuple[float, int, int]] = []
for i, ri in enumerate(ref_items):
ra = ri.get("polygon")
if not isinstance(ra, list):
continue
for j, hj in enumerate(hyp_items):
ha = hj.get("polygon")
if not isinstance(ha, list):
continueView on GitHub (pinned to 2661c7c0ef)
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.
Example fix
# before
{"results": [{"polygon": [[0,0],[1,0],[1,1],[0,1]], "text": "hi"}]}
# after
{"items": [{"polygon": [[0,0],[1,0],[1,1],[0,1]], "text": "hi"}]} Defensive patterns
Strategy: validation
Validate before calling
import json
def load_items_checked(path: str) -> list:
with open(path, encoding="utf-8") as f:
data = json.load(f)
items = data.get("items")
assert isinstance(items, list) and items, f"{path}: expected non-empty 'items' array"
return items
load_items_checked(ref_path); load_items_checked(hyp_path) Type guard
def has_items_schema(data) -> bool:
return (
isinstance(data, dict)
and isinstance(data.get("items"), list)
and all(isinstance(it, dict) for it in data["items"])
) Try / catch
try:
items = _load_items(path)
except ValueError as e:
if "missing 'items'" in str(e):
raise ValueError(f"{path}: wrong schema — expected {{\"items\": [...]}}") from e
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- compare_ocr_json.py requires `pip install shapely`
- Token is required. Set PADDLEOCR_ACCESS_TOKEN or pass token
- Destination parent must be a directory: ${parent}
- degenerate text crop (zero width/height)
- [extract_xcresult_attachments] FAIL: `xcrun xcresulttool hel
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/800d431dc934f6b0.
Report an issue: GitHub.