mvanhorn/last30days-skill · error · SystemExit

[last30days] Cannot read --synthesis-file: {exc}\n

Error message

[last30days] Cannot read --synthesis-file: {exc}\n

What it means

read_synthesis_file() loads the --synthesis-file argument with Path.expanduser().read_text(encoding='utf-8'); any OSError (missing file, permission denied, is-a-directory, unreadable bytes triggering UnicodeDecodeError-as-OSError variants) prints a [last30days] error to stderr and exits with status 2 before any search runs.

Source

Thrown at skills/last30days/scripts/last30days.py:500


def compute_output_path_display(output_file: str) -> str:
    """Compute the user-friendly explicit output path shown in render footers."""
    raw = Path(output_file).expanduser().resolve()
    try:
        home = Path.home().resolve()
        relative = raw.relative_to(home)
        return f"~/{relative.as_posix()}"
    except ValueError:
        return raw.as_posix()


def read_synthesis_file(path: str) -> str:
    try:
        return Path(path).expanduser().read_text(encoding="utf-8")
    except OSError as exc:
        sys.stderr.write(f"[last30days] Cannot read --synthesis-file: {exc}\n")
        raise SystemExit(2)


def _scoped_store_db(args: argparse.Namespace) -> Path | None:
    """Scoped runs write findings inside the save dir, matching scoped reads."""
    save_dir = getattr(args, "save_dir", None)
    if save_dir:
        return Path(save_dir).expanduser().resolve() / "research.db"
    return None


def persist_report(report: schema.Report, store_db: Path | None = None) -> dict[str, int]:
    import store

    private_corpus = _report_has_private_corpus(report)
    with store.scoped_db(store_db):
        if private_corpus:
            store.ensure_private_db_files()
        store.init_db()

View on GitHub (pinned to c7460f6114)

Solutions

  1. Verify the file exists and is readable before invoking: ls -l on the exact path.
  2. Use an absolute path (or a tilde path) for --synthesis-file to avoid cwd mismatch.
  3. If generated programmatically, ensure the write completes (and fsync/close) before the engine is spawned.

Example fix

# before
--synthesis-file /tmp/synth-$(date +%s).md   # deleted by temp cleanup

# after
SYNTH=$(mktemp /tmp/synth-XXXX.md) && cp prepared.md "$SYNTH" && \
  python3 last30days.py topic --synthesis-file "$SYNTH"
Defensive patterns

Strategy: validation

Validate before calling

p = Path(path).expanduser()
if not p.is_file():
    raise FileNotFoundError(f'--synthesis-file {p} does not exist')
if not os.access(p, os.R_OK):
    raise PermissionError(f'--synthesis-file {p} is not readable')

Try / catch

try:
    synth = read_synthesis_file(path)
except SystemExit:
    raise SystemExit(f'check --synthesis-file path/permissions: {path}')

Prevention

When it happens

Trigger: Passing --synthesis-file ~/notes/synth.md when the path does not exist, is a directory, or lacks read permission; tilde paths work (expanduser) but relative paths resolve against the process cwd, not the caller's intent.

Common situations: Agent writes the synthesis to a temp path that was cleaned up before the engine subprocess starts; typo in the path; file created by another user with restrictive permissions.

Related errors


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