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 queue_pet_repairs.py when manifest.get('jobs') is not a list. The repair flow reads imagegen-jobs.json and expects jobs to be an array of job objects; any other shape (object, null, scalar) is treated as corruption rather than an empty job set.
Source
Thrown at skills/hatch-pet/scripts/queue_pet_repairs.py:66
note = f"""
Repair attempt {attempt}:
- The previous `{state}` strip failed QA: {reason}
- Regenerate the entire row, not just one pose.
- Fill every requested frame slot with one complete centered full-body pet pose.
- Keep large gaps of pure chroma key only between slots; do not leave a requested slot empty.
- Avoid pose overlap, clipping, edge slivers, extra partial sprites, and detached fragments from neighboring poses.
- Use the canonical base image and any original references listed in `imagegen-jobs.json` as grounding inputs.
- Do not redesign the pet. Keep the exact same head shape, face design, markings, body proportions, palette, outline weight, materials, and props as the approved base pet.
- If the contact sheet shows identity drift, repair only this row while preserving the canonical base identity.
"""
prompt_path.write_text(existing.rstrip() + note.rstrip() + "\n", 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 next_archive_path(archive_dir: Path, state: str, attempt: int, suffix: str) -> Path:
candidate = archive_dir / f"{state}-attempt-{attempt}-previous{suffix}"
if not candidate.exists():
return candidate
counter = 2
while True:
candidate = archive_dir / f"{state}-attempt-{attempt}-previous-{counter}{suffix}"
if not candidate.exists():
return candidate
counter += 1
def archive_decoded_output(run_dir: Path, job: dict[str, object], state: str, attempt: int) -> str | None:
output_raw = job.get("output_path")
output = (View on GitHub (pinned to 5be4028344)
Solutions
- Re-run prepare_pet_run.py --force to regenerate a well-formed imagegen-jobs.json.
- Validate the shape: jq '.jobs | type' imagegen-jobs.json must print 'array'.
- If hand-editing, wrap the entries back into "jobs": [ ... ].
- Restore imagegen-jobs.json from version control or the prepare step's output.
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; re-run prepare") Type guard
def has_array_jobs(manifest: object) -> bool:
return isinstance(manifest, dict) and isinstance(manifest.get("jobs"), list) Prevention
- Never hand-wrap jobs as an object keyed by id; keep it an array.
- Add a jsonschema check in CI for imagegen-jobs.json.
- Version the manifest schema so producers stay aligned.
When it happens
Trigger: imagegen-jobs.json was hand-edited into an object keyed by id; a partial write left malformed JSON that still parsed but with the wrong top-level shape; a different tool wrote the manifest with a 'tasks' key instead of 'jobs'.
Common situations: Manual schema 'cleanup' that wrapped jobs in an object; downstream tool wrote the manifest with a 'tasks' key instead of 'jobs'; merge conflict resolved incorrectly.
Related errors
- review does not contain row-level results
- unknown row job id: {state}
- invalid imagegen-jobs.json: jobs must be a list
- unknown job id: {job_id}
- job {job.get('id')} does not list input_images; grounded row
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/914e76649673a144.
Report an issue: GitHub.