{"record":{"id":"0ef735d11ab2b063","repo":"mvanhorn/last30days-skill","slug":"label-capitalize-file-file-path-is-not-valid","errorCode":null,"errorMessage":"{label.capitalize()} file {file_path} is not valid JSON: {exc}","messagePattern":"(.+?) file (.+?) is not valid JSON: (.+?)","errorType":"exception","errorClass":"HandoffContractError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/discovery_handoff.py","lineNumber":627,"sourceCode":"        angle_inputs=angle_inputs,\n        mock=bool(payload.get(\"mock\")),\n        path=path,\n    )\n\n\ndef _load_host_file(path: str | Path, label: str) -> dict[str, Any]:\n    \"\"\"Load a host-authored handoff file with strict top-level checks.\"\"\"\n    file_path = Path(path).expanduser()\n    try:\n        raw = file_path.read_text(encoding=\"utf-8\")\n    except OSError as exc:\n        raise HandoffContractError(\n            f\"Could not read {label} file {file_path}: {exc}\"\n        ) from exc\n    try:\n        payload = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        raise HandoffContractError(\n            f\"{label.capitalize()} file {file_path} is not valid JSON: {exc}\"\n        ) from exc\n    if not isinstance(payload, dict):\n        raise HandoffContractError(\n            f\"{label.capitalize()} file {file_path} must be a top-level JSON \"\n            f\"object, got {type(payload).__name__}.\"\n        )\n    return payload\n\n\ndef _require_bundle_binding(\n    payload: dict[str, Any],\n    bundle: NominationsBundle | PendingReport,\n    *,\n    label: str,\n    save_dir: str | Path | None,\n    config_dir: Path | None,\n) -> None:","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/discovery_handoff.py#L609-L645","documentation":"`_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').","triggerScenarios":"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.","commonSituations":"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'.","solutions":["Run `python3 -m json.tool <file>` (or `jq . <file>`) — the error location matches the message's JSONDecodeError.","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.","If the file was mangled beyond easy repair, regenerate it (host tooling or the authoring step) rather than hand-patching.","Write future host files via `json.dump` from a script instead of by hand."],"exampleFix":"// before (judgments.json)\n{\n  'bundle_id': 'abc',  // my notes\n  'judgments': [],\n}\n// after\n{\"bundle_id\": \"abc\", \"judgments\": []}","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef host_json_parses(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    payload = discovery_handoff._load_host_file(\"judgments.json\", \"judgments\")\nexcept discovery_handoff.HandoffContractError as exc:\n    if \"not valid JSON\" in exc.message:\n        # authoring error: run python3 -m json.tool to find the syntax fault\n        ...","preventionTips":["Validate every host-authored file with `python3 -m json.tool <file>` before invoking the leg.","Generate judgments/angles files with json.dump (or the host tooling) rather than typing JSON by hand.","Paste JSON only as plain text; rich-text editors introduce smart quotes that break strict JSON parsing."],"tags":["discovery","handoff","host-file","json","authoring","exit-code-2"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}