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

Thrown by job_list() in derive_running_left_from_running_right.py when the parsed imagegen-jobs.json has a top-level 'jobs' key that is not a JSON array. The downstream logic iterates jobs as a list, so any other shape (object, string, number, null) is treated as a corrupt manifest.

Source

Thrown at skills/hatch-pet/scripts/derive_running_left_from_running_right.py:25

import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path

from PIL import Image, ImageOps


def load_manifest(run_dir: Path) -> dict[str, object]:
    path = run_dir / "imagegen-jobs.json"
    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 file_sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as file:
        for chunk in iter(lambda: file.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()

View on GitHub (pinned to 5be4028344)

Solutions

  1. Re-generate imagegen-jobs.json with the pipeline so 'jobs' is a list of job objects each carrying an 'id'.
  2. If migrating, transform the object form to a list: jobs = list(old_object.values()) and ensure each entry has an 'id' field.
  3. Validate the manifest with a JSON schema or a quick python check before invoking the script.

Example fix

// before
{"jobs": {"running-right": {...}, "running-left": {...}}}
// after
{"jobs": [{"id": "running-right", ...}, {"id": "running-left", "mirror_policy": {"may_derive_from": "running-right"}}]}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
def load_jobs(run_dir):
    data = json.loads((run_dir / "imagegen-jobs.json").read_text(encoding="utf-8"))
    jobs = data.get("jobs")
    if not isinstance(jobs, list):
        raise SystemExit("jobs must be a list")
    return jobs

Type guard

def is_valid_manifest(data) -> bool:
    return isinstance(data, dict) and isinstance(data.get("jobs"), list) and all(isinstance(j, dict) for j in data["jobs"])

Prevention

When it happens

Trigger: Run derive_running_left_from_running_right.py --run-dir <dir> where imagegen-jobs.json exists but its 'jobs' field is not a list — e.g. it is an object keyed by job id, or the file was hand-edited/truncated.

Common situations: Manifest written by a different version of the pipeline that used an object keyed by id; partial/corrupted JSON write; manual edit that wrapped jobs in another object.

Related errors


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