{"record":{"id":"1d1fd4212dcefd4e","repo":"nexu-io/open-design","slug":"job-job-id-has-no-field","errorCode":null,"errorMessage":"job {job_id} has no {field}","messagePattern":"job (.+?) has no (.+?)","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"skills/hatch-pet/scripts/finalize_pet_run.py","lineNumber":52,"sourceCode":"def is_relative_to(path: Path, root: Path) -> bool:\n    try:\n        path.relative_to(root)\n    except ValueError:\n        return False\n    return True\n\n\ndef default_generated_images_root() -> Path:\n    return default_codex_home() / \"generated_images\"\n\n\ndef default_codex_home() -> Path:\n    return Path(os.environ.get(\"CODEX_HOME\") or \"~/.codex\").expanduser().resolve()\n\n\ndef manifest_path(raw: object, *, run_dir: Path, field: str, job_id: str) -> Path:\n    if not isinstance(raw, str) or not raw:\n        raise SystemExit(f\"job {job_id} has no {field}\")\n    path = Path(raw).expanduser()\n    if not path.is_absolute():\n        path = run_dir / path\n    return path.resolve()\n\n\ndef validate_hash(job: dict[str, object], *, source: Path, output: Path, job_id: str) -> None:\n    expected_hash = job.get(\"source_sha256\")\n    if not isinstance(expected_hash, str) or not expected_hash:\n        raise SystemExit(\n            f\"job {job_id} is missing source_sha256; ingest visual outputs with \"\n            \"record_imagegen_result.py instead of editing imagegen-jobs.json\"\n        )\n    if not source.is_file():\n        raise SystemExit(f\"job {job_id} source image no longer exists: {source}\")\n    if not output.is_file():\n        raise SystemExit(f\"job {job_id} decoded output is missing: {output}\")\n    source_hash = file_sha256(source)","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/finalize_pet_run.py#L34-L70","documentation":"manifest_path() requires every completed job to have a non-empty source_path and output_path string in imagegen-jobs.json. The {field} in the message is whichever of source_path or output_path is missing. Without both, finalize cannot locate the files to hash-check provenance.","triggerScenarios":"A job whose status is \"complete\" but whose source_path or output_path is absent, empty, or not a string in the manifest.","commonSituations":"Hand-editing imagegen-jobs.json instead of using record_imagegen_result.py; a partially-written manifest from a killed ingest; an older manifest format missing output_path.","solutions":["Re-ingest the job with record_imagegen_result.py, which populates source_path and output_path.","If editing manually, ensure both source_path and output_path are non-empty strings (absolute, or relative to run_dir).","Re-run finalize_pet_run.py."],"exampleFix":"// before\n{ \"id\": \"idle\", \"status\": \"complete\", \"source_path\": \"\" }\n// after\n{ \"id\": \"idle\", \"status\": \"complete\", \"source_path\": \"<generated_images>/ig_xxx.png\", \"output_path\": \"decoded/idle.png\" }","handlingStrategy":"type-guard","validationCode":"import json\nfrom pathlib import Path\n\nmanifest = json.loads(Path(\"<run_dir>/imagegen-jobs.json\").read_text())\nfor job in manifest[\"jobs\"]:\n    if job.get(\"status\") == \"complete\":\n        for field in (\"source_path\", \"output_path\"):\n            assert isinstance(job.get(field), str) and job[field], f\"job {job.get('id')} has no {field}\"","typeGuard":"def job_has_paths(job: dict) -> bool:\n    return all(isinstance(job.get(f), str) and job[f] for f in (\"source_path\", \"output_path\"))","tryCatchPattern":null,"preventionTips":["Always ingest via record_imagegen_result.py rather than editing imagegen-jobs.json by hand.","After any manual edit, re-check that every complete job has non-empty source_path and output_path.","Treat a missing path as a sign the ingest was interrupted; re-run it."],"tags":["hatch-pet","manifest","validation","provenance"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}