{"record":{"id":"89e142b007e24b42","repo":"mvanhorn/last30days-skill","slug":"label-path-is-not-valid-json-exc","errorCode":null,"errorMessage":"{label} {path} is not valid JSON: {exc}","messagePattern":"(.+?) (.+?) is not valid JSON: (.+?)","errorType":"exception","errorClass":"HandoffContractError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/discovery_handoff.py","lineNumber":404,"sourceCode":"    schema_version: str,\n    remedy: str,\n    missing_id_context: str,\n    stale_context: str,\n) -> tuple[dict[str, Any], str, Any]:\n    \"\"\"Shared strict top-level validation for the two engine-written handoff\n    files (nominations bundle, pending report): readable, valid JSON object,\n    right kind and schema version, bundle_id present, within TTL. Returns\n    (payload, bundle_id, generated_at).\"\"\"\n    try:\n        raw = path.read_text(encoding=\"utf-8\")\n    except OSError as exc:\n        raise HandoffContractError(\n            f\"Could not read {label.lower()} {path}: {exc}\"\n        ) from exc\n    try:\n        payload = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        raise HandoffContractError(\n            f\"{label} {path} is not valid JSON: {exc}\"\n        ) from exc\n    if not isinstance(payload, dict):\n        raise HandoffContractError(\n            f\"{label} {path} must be a top-level JSON object, \"\n            f\"got {type(payload).__name__}.\"\n        )\n    version = payload.get(\"schema_version\")\n    if version != schema_version:\n        raise HandoffContractError(\n            f\"{label} {path} has schema version {version!r}; this \"\n            f\"build reads {schema_version!r}. {remedy}\"\n        )\n    file_kind = payload.get(\"kind\")\n    if file_kind != kind:\n        raise HandoffContractError(\n            f\"{label} {path} has kind {file_kind!r}; expected \"\n            f\"{kind!r}. {remedy}\"","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/discovery_handoff.py#L386-L422","documentation":"`_parse_handoff_envelope` raises HandoffContractError when the handoff file's bytes fail `json.loads` — the file is readable but not valid JSON (truncation, BOM/encoding damage, hand-edited corruption, or a partial write from a crashed leg). The underlying JSONDecodeError (with line/column) is chained and shown, so the exact corruption point is identifiable.","triggerScenarios":"`discover-nominations.json` or `discover-pending.json` truncated by a kill -9 mid-write; saved with a UTF-8 BOM; edited by hand and given a trailing comma; overwritten by another tool's non-JSON output (e.g. an error page).","commonSituations":"Interrupting a run during bundle write (the write is not atomic); version-control merge conflicts left unresolved inside the JSON; sync tools (Dropbox) uploading a partial file; humans 'fixing' the bundle in an editor.","solutions":["Use the JSONDecodeError position in the message (`jq <path>` or `python3 -m json.tool <path>` reproduces it) to see the corruption point.","If truncated/corrupt, discard the file and re-run the producing leg: `--discover --nominate-only` (bundle) or `--discover --judgments <file>` (pending report) — regeneration is the intended remedy, not hand repair.","Re-author host judgments after regenerating: the new bundle has a new bundle_id and old judgments will not bind.","Avoid editing handoff files; they are engine-written contract state."],"exampleFix":"# before: interrupted sweep left truncated JSON\n# after\nrm /path/save-dir/discover-nominations.json && python3 last30days.py \"topic\" --discover --nominate-only --save-dir /path/save-dir","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef is_valid_json_file(path: Path) -> bool:\n    try:\n        json.loads(path.read_text(encoding=\"utf-8\"))\n        return True\n    except (OSError, json.JSONDecodeError):\n        return False","typeGuard":null,"tryCatchPattern":"from lib import discovery_handoff\ntry:\n    bundle = discovery_handoff.load_nominations_bundle(save_dir=save_dir, config_dir=config_dir)\nexcept discovery_handoff.HandoffContractError as exc:\n    if \"not valid JSON\" in exc.message:\n        # corrupt: regenerate via --discover --nominate-only, do not hand-repair\n        ...","preventionTips":["Never edit engine-written handoff files; regenerate them with the producing leg instead.","Keep sync tools (Dropbox/Drive) away from the save dir — partial uploads corrupt these files.","Let leg 1 finish writing before interrupting a run."],"tags":["discovery","handoff","json","corruption","exit-code-2"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}