{"record":{"id":"be452732aaa5326a","repo":"nexu-io/open-design","slug":"invalid-imagegen-jobs-json-jobs-must-be-a-list-be4527","errorCode":null,"errorMessage":"invalid imagegen-jobs.json: jobs must be a list","messagePattern":"invalid imagegen-jobs\\.json: jobs must be a list","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"skills/hatch-pet/scripts/record_imagegen_result.py","lineNumber":28,"sourceCode":"import shutil\nfrom datetime import datetime, timezone\nfrom pathlib import Path\n\nfrom PIL import Image\n\nCANONICAL_BASE_PATH = \"references/canonical-base.png\"\n\n\ndef load_jobs(path: Path) -> dict[str, object]:\n    if not path.exists():\n        raise SystemExit(f\"job manifest not found: {path}\")\n    return json.loads(path.read_text(encoding=\"utf-8\"))\n\n\ndef job_list(manifest: dict[str, object]) -> list[dict[str, object]]:\n    jobs = manifest.get(\"jobs\")\n    if not isinstance(jobs, list):\n        raise SystemExit(\"invalid imagegen-jobs.json: jobs must be a list\")\n    return [job for job in jobs if isinstance(job, dict)]\n\n\ndef find_job(manifest: dict[str, object], job_id: str) -> dict[str, object]:\n    for job in job_list(manifest):\n        if job.get(\"id\") == job_id:\n            return job\n    raise SystemExit(f\"unknown job id: {job_id}\")\n\n\ndef image_metadata(path: Path) -> dict[str, object]:\n    with Image.open(path) as image:\n        image.verify()\n    with Image.open(path) as image:\n        return {\n            \"width\": image.width,\n            \"height\": image.height,\n            \"mode\": image.mode,","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/record_imagegen_result.py#L10-L46","documentation":"Raised by job_list() in record_imagegen_result.py when manifest.get('jobs') is not a list. The recorder iterates jobs to find the target, compute completed dependencies, and validate grounding; any non-array top-level jobs field is treated as corruption. This is the same schema guard as in queue_pet_repairs.py, duplicated per-script.","triggerScenarios":"Hand-edited manifest that wrapped jobs in an object; a partial/failed write left JSON that parses but with the wrong shape; a different producer wrote the manifest with a non-array jobs field.","commonSituations":"Manual schema 'cleanup'; merge conflict resolved into an object; downstream tool emitted a different key.","solutions":["Re-run prepare_pet_run.py --force to regenerate a well-formed imagegen-jobs.json.","Validate: jq '.jobs | type' imagegen-jobs.json must print 'array'.","If editing manually, ensure \"jobs\": [ {...}, {...} ].","Restore from version control."],"exampleFix":"// before: {\"jobs\": {\"idle\": {...}}}\n// after:  {\"jobs\": [{\"id\": \"idle\", ...}]}","handlingStrategy":"type-guard","validationCode":"jobs = manifest.get(\"jobs\")\nif not isinstance(jobs, list):\n    raise SystemExit(\"imagegen-jobs.json 'jobs' must be an array\")","typeGuard":"def has_array_jobs(manifest: object) -> bool:\n    return isinstance(manifest, dict) and isinstance(manifest.get(\"jobs\"), list)","tryCatchPattern":null,"preventionTips":["Treat prepare_pet_run.py as the single writer of imagegen-jobs.json.","Validate the manifest shape in CI.","Avoid manual edits to the jobs field."],"tags":["data-validation","cli","hatch-pet","schema"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}