{"record":{"id":"3a6a02ed35299911","repo":"nexu-io/open-design","slug":"invalid-imagegen-jobs-json-jobs-must-be-a-list-3a6a02","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/finalize_pet_run.py","lineNumber":186,"sourceCode":"        raise SystemExit(\n            f\"job {job_id} source image is inside the pet run directory; \"\n            \"do not use locally generated row artifacts as visual sources\"\n        )\n    generated_root = default_generated_images_root()\n    if not is_relative_to(source, generated_root) or not source.name.startswith(\"ig_\"):\n        raise SystemExit(\n            f\"job {job_id} source image is not a built-in $imagegen output under \"\n            f\"{generated_root}/.../ig_*.png\"\n        )\n    validate_hash(job, source=source, output=output, job_id=job_id)\n\n\ndef require_complete_jobs(run_dir: Path, *, allow_synthetic_test_sources: bool) -> None:\n    manifest_path = run_dir / \"imagegen-jobs.json\"\n    manifest = load_json(manifest_path)\n    jobs = manifest.get(\"jobs\")\n    if not isinstance(jobs, list):\n        raise SystemExit(\"invalid imagegen-jobs.json: jobs must be a list\")\n    incomplete = [\n        str(job.get(\"id\"))\n        for job in jobs\n        if isinstance(job, dict) and job.get(\"status\", \"pending\") != \"complete\"\n    ]\n    if incomplete:\n        raise SystemExit(\n            \"imagegen jobs are not complete; run pet_job_status.py and finish: \"\n            + \", \".join(incomplete)\n        )\n    for job in jobs:\n        if isinstance(job, dict):\n            validate_completed_job_source(\n                job,\n                run_dir=run_dir,\n                allow_synthetic_test_sources=allow_synthetic_test_sources,\n            )\n","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/finalize_pet_run.py#L168-L204","documentation":"Raised by require_complete_jobs in finalize_pet_run.py when imagegen-jobs.json parses but its 'jobs' field is not a JSON list. The manifest schema requires jobs to be an array of job objects; any other shape is treated as corruption.","triggerScenarios":"json.loads(run_dir/'imagegen-jobs.json') succeeds but manifest.get('jobs') is a dict, string, number, null, or missing.","commonSituations":"Hand-editing imagegen-jobs.json and accidentally turning the jobs array into an object keyed by id; a partial write that left 'jobs' as null; schema drift from an older manifest version.","solutions":["Open imagegen-jobs.json and ensure the top-level 'jobs' key is a JSON array of job objects.","Regenerate the manifest from the job-creation step that originally produced it.","Validate the file with `python -c \"import json;print(type(json.load(open('imagegen-jobs.json'))['jobs']))\"` before re-running finalize."],"exampleFix":"// before (imagegen-jobs.json)\n{\"jobs\":{\"base\":{...}}}\n// after\n{\"jobs\":[{\"id\":\"base\",...}]}","handlingStrategy":"type-guard","validationCode":"manifest = json.loads(Path('imagegen-jobs.json').read_text())\nif not isinstance(manifest.get('jobs'), list):\n    raise ValueError('imagegen-jobs.json jobs must be a list')","typeGuard":"from typing import Any, TypeGuard\ndef is_job_list(manifest: Any) -> TypeGuard[dict[str, list[dict[str, object]]]]:\n    return isinstance(manifest, dict) and isinstance(manifest.get('jobs'), list)","tryCatchPattern":null,"preventionTips":["Validate imagegen-jobs.json against a JSON schema with jobs: array before running finalize.","Never hand-edit the jobs field into a different shape.","Regenerate corrupted manifests from the job-creation step."],"tags":["manifest","schema","json","pet-pipeline"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}