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
- Verify the file exists and is readable before invoking: ls -l on the exact path.
- Use an absolute path (or a tilde path) for --synthesis-file to avoid cwd mismatch.
- 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
- Use absolute paths for file arguments.
- Ensure generated synthesis files are fully written before spawning the engine.
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
- Unknown search source in {flag_name}: {source}
- {flag_name} requires at least one source.
- Unsupported emit mode: {emit}
- [CompetitorsPlan] Cannot read plan file: {exc}\n
- [Competitors] --competitors-list is empty.\n
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/004c3720f559f2fe.
Report an issue: GitHub.