odysseus-dev/odysseus · error · ValueError

could not read {path}: {exc}

Error message

could not read {path}: {exc}

What it means

Raised by load_json_file in scripts/pr_blocker_audit.py when opening or reading the given path raises OSError — the file does not exist, is a directory, or the process lacks read permission. Like the JSON error, it is normalized to ValueError with the path embedded, so the audit script reports a uniform error for unusable input files.

Source

Thrown at scripts/pr_blocker_audit.py:161

            self.stream.write(f"\r{' ' * self.last_len}\r")
            self.stream.flush()
            self.last_len = 0

    def summary(self, message: str) -> None:
        if self.enabled:
            self.finish_line()
            self.stream.write(f"{message}\n")
            self.stream.flush()


def load_json_file(path: Path):
    try:
        with path.open("r", encoding="utf-8") as handle:
            return json.load(handle)
    except json.JSONDecodeError as exc:
        raise ValueError(f"invalid JSON in {path}: {exc.msg} at line {exc.lineno}, column {exc.colno}") from exc
    except OSError as exc:
        raise ValueError(f"could not read {path}: {exc}") from exc


def fetch_live_prs(repo: str, fetch_files: bool = True, progress: ProgressReporter | None = None, limit: int = 1000):
    progress = progress or ProgressReporter(False)
    fields = (
        "number,title,author,files,mergeStateStatus,reviewDecision,updatedAt,url"
        if fetch_files
        else "number,title,author,mergeStateStatus,reviewDecision,updatedAt,url"
    )
    cmd = ["gh", "pr", "list", "--repo", repo, "--state", "open", "--limit", str(limit), "--json", fields]
    progress.phase("Fetching open PR list...")
    try:
        payload = _run_gh_json(cmd)
    except RuntimeError:
        api_path = f"repos/{repo}/pulls?state=open&per_page=100"
        payload = _run_gh_json(["gh", "api", "--paginate", api_path])
        payload = _limit_payload(payload, limit)
    if not fetch_files:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Confirm the file exists at the exact path: ls -l <path> from the same directory you run the script from.
  2. Use an absolute path to eliminate cwd ambiguity.
  3. Fix permissions (chmod +r) or copy the file somewhere readable if running in a restricted environment.
  4. Expand any ~ or $VAR manually: use realpath <path> to get the literal location.

Example fix

# before
$ python scripts/pr_blocker_audit.py --input prs.json   # run from another dir
ValueError: could not read prs.json: [Errno 2] No such file or directory

# after
$ python scripts/pr_blocker_audit.py --input /abs/path/to/prs.json
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def check_readable_file(path: str) -> Path:
    p = Path(path).expanduser().resolve()
    if not p.is_file():
        raise FileNotFoundError(f'not a file: {p}')
    if not p.stat().st_size:
        raise ValueError(f'empty file: {p}')
    return p

Type guard

import os

def is_readable_file(path: str) -> bool:
    return os.path.isfile(os.path.expanduser(path)) and os.access(os.path.expanduser(path), os.R_OK)

Try / catch

try:
    payload = load_json_file(Path(args.input))
except ValueError as e:
    if 'could not read' in str(e):
        print(f'Check path/permissions: {e}', file=sys.stderr)
    raise SystemExit(2)

Prevention

When it happens

Trigger: Passing a wrong/misspelled --input path; relative path resolved from a different working directory; pointing at a directory instead of a file; permission bits or sandboxing denying read access; a path containing an unexpanded ~ or shell variable.

Common situations: Running the script from a different cwd so a relative snapshot path misses; typos in long filenames; the snapshot file was moved or deleted between capture and audit; running under a restricted container without volume read access.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/c4c75f05572ff241. Report an issue: GitHub.