mvanhorn/last30days-skill · error · HandoffContractError

{label.capitalize()} file {file_path} is not valid JSON: {ex

Error message

{label.capitalize()} file {file_path} is not valid JSON: {exc}

What it means

`_load_host_file` raises HandoffContractError when the host-authored judgments/angles file is readable but fails `json.loads`. Because these files are hand- or model-authored (unlike the engine-written bundle/pending artifacts), the usual causes are authoring mistakes: trailing commas, comments, single quotes, smart quotes from rich-text editors, or truncation. The chained JSONDecodeError pinpoints the line/column; the label is capitalized in the message (e.g. 'Judgments file ... is not valid JSON').

Source

Thrown at skills/last30days/scripts/lib/discovery_handoff.py:627

        angle_inputs=angle_inputs,
        mock=bool(payload.get("mock")),
        path=path,
    )


def _load_host_file(path: str | Path, label: str) -> dict[str, Any]:
    """Load a host-authored handoff file with strict top-level checks."""
    file_path = Path(path).expanduser()
    try:
        raw = file_path.read_text(encoding="utf-8")
    except OSError as exc:
        raise HandoffContractError(
            f"Could not read {label} file {file_path}: {exc}"
        ) from exc
    try:
        payload = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise HandoffContractError(
            f"{label.capitalize()} file {file_path} is not valid JSON: {exc}"
        ) from exc
    if not isinstance(payload, dict):
        raise HandoffContractError(
            f"{label.capitalize()} file {file_path} must be a top-level JSON "
            f"object, got {type(payload).__name__}."
        )
    return payload


def _require_bundle_binding(
    payload: dict[str, Any],
    bundle: NominationsBundle | PendingReport,
    *,
    label: str,
    save_dir: str | Path | None,
    config_dir: Path | None,
) -> None:

View on GitHub (pinned to c7460f6114)

Solutions

  1. Run `python3 -m json.tool <file>` (or `jq . <file>`) — the error location matches the message's JSONDecodeError.
  2. Fix the specific syntax issue: remove trailing commas/comments, use double quotes for all strings and keys, ensure the whole document is one JSON value.
  3. If the file was mangled beyond easy repair, regenerate it (host tooling or the authoring step) rather than hand-patching.
  4. Write future host files via `json.dump` from a script instead of by hand.

Example fix

// before (judgments.json)
{
  'bundle_id': 'abc',  // my notes
  'judgments': [],
}
// after
{"bundle_id": "abc", "judgments": []}
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def host_json_parses(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:
    payload = discovery_handoff._load_host_file("judgments.json", "judgments")
except discovery_handoff.HandoffContractError as exc:
    if "not valid JSON" in exc.message:
        # authoring error: run python3 -m json.tool to find the syntax fault
        ...

Prevention

When it happens

Trigger: A judgments file with a trailing comma or `// comment`; copy-pasting JSON through a chat/markdown renderer that swapped straight quotes for curly ones; saving the file mid-edit; writing YAML or a Python-literal dict instead of JSON.

Common situations: LLM hosts emitting JSON with unquoted keys or single quotes; editors auto-inserting BOMs; users annotating the file with comments; `print()` of a Python dict saved as 'json'.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/0ef735d11ab2b063. Report an issue: GitHub.