mvanhorn/last30days-skill · error · HandoffContractError
{label} {path} is not valid JSON: {exc}
Error message
{label} {path} is not valid JSON: {exc} What it means
`_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.
Source
Thrown at skills/last30days/scripts/lib/discovery_handoff.py:404
schema_version: str,
remedy: str,
missing_id_context: str,
stale_context: str,
) -> tuple[dict[str, Any], str, Any]:
"""Shared strict top-level validation for the two engine-written handoff
files (nominations bundle, pending report): readable, valid JSON object,
right kind and schema version, bundle_id present, within TTL. Returns
(payload, bundle_id, generated_at)."""
try:
raw = path.read_text(encoding="utf-8")
except OSError as exc:
raise HandoffContractError(
f"Could not read {label.lower()} {path}: {exc}"
) from exc
try:
payload = json.loads(raw)
except json.JSONDecodeError as exc:
raise HandoffContractError(
f"{label} {path} is not valid JSON: {exc}"
) from exc
if not isinstance(payload, dict):
raise HandoffContractError(
f"{label} {path} must be a top-level JSON object, "
f"got {type(payload).__name__}."
)
version = payload.get("schema_version")
if version != schema_version:
raise HandoffContractError(
f"{label} {path} has schema version {version!r}; this "
f"build reads {schema_version!r}. {remedy}"
)
file_kind = payload.get("kind")
if file_kind != kind:
raise HandoffContractError(
f"{label} {path} has kind {file_kind!r}; expected "
f"{kind!r}. {remedy}"View on GitHub (pinned to c7460f6114)
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.
Example fix
# before: interrupted sweep left truncated JSON # after rm /path/save-dir/discover-nominations.json && python3 last30days.py "topic" --discover --nominate-only --save-dir /path/save-dir
Defensive patterns
Strategy: validation
Validate before calling
import json
from pathlib import Path
def is_valid_json_file(path: Path) -> bool:
try:
json.loads(path.read_text(encoding="utf-8"))
return True
except (OSError, json.JSONDecodeError):
return False Try / catch
from lib import discovery_handoff
try:
bundle = discovery_handoff.load_nominations_bundle(save_dir=save_dir, config_dir=config_dir)
except discovery_handoff.HandoffContractError as exc:
if "not valid JSON" in exc.message:
# corrupt: regenerate via --discover --nominate-only, do not hand-repair
... Prevention
- 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.
When it happens
Trigger: `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).
Common situations: 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.
Related errors
- {label} {path} must be a top-level JSON object, got {type(pa
- Nominations bundle {path} must carry a top-level "nomination
- Nominations bundle {path} contains no readable nominations (
- Pending discovery report {path} must carry a top-level \"rep
- {label.capitalize()} file {file_path} is not valid JSON: {ex
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/89e142b007e24b42.
Report an issue: GitHub.