mvanhorn/last30days-skill · error · HandoffContractError

Could not read {label} file {file_path}: {exc}

Error message

Could not read {label} file {file_path}: {exc}

What it means

`_load_host_file` (reader for host-authored handoff files: the leg-2 `--judgments` file and leg-3 `--angles` file) raises HandoffContractError when `Path(path).expanduser().read_text()` raises OSError — the file does not exist, is unreadable, or the path is invalid. Unlike the engine-written artifacts, this file is authored by the host/user, so the common case is simply a wrong path or missing file. The message includes the label ('judgments'/'angles'), the expanded path, and the OS error; the CLI maps it to exit 2.

Source

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

    return PendingReport(
        schema_version=str(version),
        bundle_id=bundle_id,
        generated_at=str(generated_at or ""),
        run_ref=str(payload.get("run_ref") or ""),
        report=report,
        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],

View on GitHub (pinned to c7460f6114)

Solutions

  1. Verify the path exactly as printed in the message with `ls -l` (the message shows the expanded path).
  2. Use an absolute path for `--judgments` / `--angles` to eliminate cwd ambiguity.
  3. Confirm the file was actually saved/written by the authoring step before invoking the leg.
  4. If permissions are the issue, `chmod +r <file>`.

Example fix

# before
python3 last30days.py "topic" --discover --judgments judgemnets.json   # typo
# after
python3 last30days.py "topic" --discover --judgments /abs/path/judgments.json
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def host_file_ready(path_str: str) -> Path:
    p = Path(path_str).expanduser().resolve()
    if not p.is_file():
        raise SystemExit(f"{p} does not exist; author the file first")
    return p

Try / catch

from lib import discovery_handoff
try:
    payload = discovery_handoff._load_host_file("judgments.json", "judgments")
except discovery_handoff.HandoffContractError as exc:
    if "Could not read" in exc.message:
        # wrong path or permissions; the message shows the expanded path
        ...

Prevention

When it happens

Trigger: `--judgments missing.json` (typo'd filename); passing a directory instead of a file; permissions denying read; a `~`-relative path that fails to expand to a real file.

Common situations: Authoring judgments in an editor and never saving; running the CLI from a different cwd with a relative path; host harnesses writing the file to an unexpected location; quoting mistakes leaving a literal `~` unexpanded by the shell (here expanduser() handles it, but a wrong username like `~other/file` may not exist).

Related errors


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