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 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.

Source

Thrown at skills/hatch-pet/scripts/record_imagegen_result.py:28

import shutil
from datetime import datetime, timezone
from pathlib import Path

from PIL import Image

CANONICAL_BASE_PATH = "references/canonical-base.png"


def load_jobs(path: Path) -> dict[str, object]:
    if not path.exists():
        raise SystemExit(f"job manifest not found: {path}")
    return json.loads(path.read_text(encoding="utf-8"))


def job_list(manifest: dict[str, object]) -> list[dict[str, object]]:
    jobs = manifest.get("jobs")
    if not isinstance(jobs, list):
        raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
    return [job for job in jobs if isinstance(job, dict)]


def find_job(manifest: dict[str, object], job_id: str) -> dict[str, object]:
    for job in job_list(manifest):
        if job.get("id") == job_id:
            return job
    raise SystemExit(f"unknown job id: {job_id}")


def image_metadata(path: Path) -> dict[str, object]:
    with Image.open(path) as image:
        image.verify()
    with Image.open(path) as image:
        return {
            "width": image.width,
            "height": image.height,
            "mode": image.mode,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Re-run prepare_pet_run.py --force to regenerate a well-formed imagegen-jobs.json.
  2. Validate: jq '.jobs | type' imagegen-jobs.json must print 'array'.
  3. If editing manually, ensure "jobs": [ {...}, {...} ].
  4. Restore from version control.

Example fix

// before: {"jobs": {"idle": {...}}}
// after:  {"jobs": [{"id": "idle", ...}]}
Defensive patterns

Strategy: type-guard

Validate before calling

jobs = manifest.get("jobs")
if not isinstance(jobs, list):
    raise SystemExit("imagegen-jobs.json 'jobs' must be an array")

Type guard

def has_array_jobs(manifest: object) -> bool:
    return isinstance(manifest, dict) and isinstance(manifest.get("jobs"), list)

Prevention

When it happens

Trigger: 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.

Common situations: Manual schema 'cleanup'; merge conflict resolved into an object; downstream tool emitted a different key.

Related errors


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