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 jobs() in pet_job_status.py (and the parallel check in generate_pet_images.py:51) when imagegen-jobs.json parses but its top-level jobs field is not a list. The contract is {schema_version, jobs: [...]}, so a non-list jobs value is treated as corruption.

Source

Thrown at skills/hatch-pet/scripts/pet_job_status.py:21

from __future__ import annotations

import argparse
import json
from pathlib import Path


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 jobs(manifest: dict[str, object]) -> list[dict[str, object]]:
    raw = manifest.get("jobs")
    if not isinstance(raw, list):
        raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
    return [job for job in raw if isinstance(job, dict)]


def completed_ids(manifest: dict[str, object]) -> set[str]:
    return {
        str(job["id"])
        for job in jobs(manifest)
        if job.get("status") == "complete" and isinstance(job.get("id"), str)
    }


def missing_deps(job: dict[str, object], completed: set[str]) -> list[str]:
    deps = job.get("depends_on", [])
    if not isinstance(deps, list):
        return []
    return [dep for dep in deps if isinstance(dep, str) and dep not in completed]

View on GitHub (pinned to 5be4028344)

Solutions

  1. Open imagegen-jobs.json and confirm jobs is a JSON array of job objects.
  2. If it is keyed by id, convert to a list, or regenerate with prepare_pet_run.py.
  3. Validate JSON: python -c "import json; d=json.load(open('imagegen-jobs.json')); print(type(d['jobs']).__name__)" should print list.
  4. Re-run the status/generation command.

Example fix

// before (manifest)
{"jobs": {"base": {...}, "idle": {...}}}

// after
{"schema_version": 1, "jobs": [{"id": "base", ...}, {"id": "idle", ...}]}
Defensive patterns

Strategy: type-guard

Validate before calling

def assert_jobs_list(manifest: dict) -> list:
    raw = manifest.get("jobs")
    if not isinstance(raw, list):
        raise SystemExit("invalid imagegen-jobs.json: jobs must be a list")
    return raw

Type guard

from typing import Any

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

Prevention

When it happens

Trigger: imagegen-jobs.json was hand-edited so jobs is a dict or object keyed by id; a partial write left jobs as null; a different schema version nested jobs elsewhere.

Common situations: Restructuring the manifest to {jobs: {idle: {...}}} instead of a list; truncated write from a crashed run; manifest produced by an incompatible tool.

Related errors


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