nexu-io/open-design · error · SystemExit

invalid imagegen-jobs.json: jobs must be a list

Error message

invalid imagegen-jobs.json: jobs must be a list

What it means

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.

Source

Thrown at skills/hatch-pet/scripts/finalize_pet_run.py:186

        raise SystemExit(
            f"job {job_id} source image is inside the pet run directory; "
            "do not use locally generated row artifacts as visual sources"
        )
    generated_root = default_generated_images_root()
    if not is_relative_to(source, generated_root) or not source.name.startswith("ig_"):
        raise SystemExit(
            f"job {job_id} source image is not a built-in $imagegen output under "
            f"{generated_root}/.../ig_*.png"
        )
    validate_hash(job, source=source, output=output, job_id=job_id)


def require_complete_jobs(run_dir: Path, *, allow_synthetic_test_sources: bool) -> None:
    manifest_path = run_dir / "imagegen-jobs.json"
    manifest = load_json(manifest_path)
    jobs = manifest.get("jobs")
    if not isinstance(jobs, list):
        raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
    incomplete = [
        str(job.get("id"))
        for job in jobs
        if isinstance(job, dict) and job.get("status", "pending") != "complete"
    ]
    if incomplete:
        raise SystemExit(
            "imagegen jobs are not complete; run pet_job_status.py and finish: "
            + ", ".join(incomplete)
        )
    for job in jobs:
        if isinstance(job, dict):
            validate_completed_job_source(
                job,
                run_dir=run_dir,
                allow_synthetic_test_sources=allow_synthetic_test_sources,
            )

View on GitHub (pinned to 5be4028344)

Solutions

  1. Open imagegen-jobs.json and ensure the top-level 'jobs' key is a JSON array of job objects.
  2. Regenerate the manifest from the job-creation step that originally produced it.
  3. Validate the file with `python -c "import json;print(type(json.load(open('imagegen-jobs.json'))['jobs']))"` before re-running finalize.

Example fix

// before (imagegen-jobs.json)
{"jobs":{"base":{...}}}
// after
{"jobs":[{"id":"base",...}]}
Defensive patterns

Strategy: type-guard

Validate before calling

manifest = json.loads(Path('imagegen-jobs.json').read_text())
if not isinstance(manifest.get('jobs'), list):
    raise ValueError('imagegen-jobs.json jobs must be a list')

Type guard

from typing import Any, TypeGuard
def is_job_list(manifest: Any) -> TypeGuard[dict[str, list[dict[str, object]]]]:
    return isinstance(manifest, dict) and isinstance(manifest.get('jobs'), list)

Prevention

When it happens

Trigger: json.loads(run_dir/'imagegen-jobs.json') succeeds but manifest.get('jobs') is a dict, string, number, null, or missing.

Common situations: 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.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/3a6a02ed35299911. Report an issue: GitHub.