mvanhorn/last30days-skill · error · RuntimeError
_save_output: could not find a unique filename after 101 att
Error message
_save_output: could not find a unique filename after 101 attempts in {path} What it means
Raised by _save_output in hosted.py when all 101 candidate filenames (the base name plus suffixes -1..-100) already exist in the save directory. The writer uses os.open with O_CREAT|O_EXCL to guarantee atomic no-clobber creation; if every candidate collides it gives up rather than overwrite. In practice this means the same slug/date/extension combination has been saved ~100 times.
Source
Thrown at skills/last30days/scripts/lib/hosted.py:232
extension = "json" if emit == "json" else "md"
suffix_part = f"-{suffix}" if suffix else ""
base = path / f"{slug}-raw{suffix_part}.{extension}"
date_str = datetime.now().strftime('%Y-%m-%d')
candidates = [base]
candidates.append(path / f"{slug}-raw{suffix_part}-{date_str}.{extension}")
for i in range(1, 100):
candidates.append(path / f"{slug}-raw{suffix_part}-{date_str}-{i}.{extension}")
encoded = content.encode("utf-8")
for candidate in candidates:
try:
fd = os.open(candidate, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
except FileExistsError:
continue
with os.fdopen(fd, "wb") as f:
f.write(encoded)
return candidate
# Fallback: all 101 candidates existed (extremely unlikely).
raise RuntimeError(
f"_save_output: could not find a unique filename after 101 attempts in {path}"
)
def _render_complete(row: dict, topic: str, emit: str, save_dir, save_suffix: str) -> int:
synthesis = row.get("synthesis_text") or ""
raw_markdown = row.get("raw_markdown") or ""
if emit == "json":
payload = {
key: row.get(key)
for key in ("id", "status", "synthesis_text", "raw_markdown")
if key in row
}
rendered = json.dumps(payload, indent=2, sort_keys=True)
save_content = rendered
else:
# The server report is the content source; it already synthesized.
# All markdown-ish emit modes print the synthesis text as-is.View on GitHub (pinned to c7460f6114)
Solutions
- Delete or archive the existing 100+ collision files for that slug in the save directory.
- Vary the inputs: use a distinct --save-suffix, topic slug, or output directory for repeated runs.
- Fix the caller that re-saves the identical artifact in a loop (the collision count is the symptom, the loop is the bug).
Example fix
# before: repeated identical invocations
for i in range(150):
run(slug="my-topic", suffix="", out_dir=same_dir) # exhausts -1..-99
# after: unique suffix per run
for i in range(150):
run(slug="my-topic", suffix=f"v{i}", out_dir=same_dir) Defensive patterns
Strategy: fallback
Validate before calling
from pathlib import Path
def can_save(path: Path, base_candidates: list[str]) -> bool:
return not all((path / c).exists() for c in base_candidates) Try / catch
try:
out = _save_output(...)
except RuntimeError as e:
if 'unique filename' in str(e):
# switch save_dir or make the slug/suffix unique, then retry once
out = _save_output(..., suffix=f"{suffix}-{run_token}") Prevention
- Make repeated runs unique by construction: include a run token or timestamp in save_suffix.
- Alert when a single slug/date accumulates dozens of files — that indicates a retry loop bug.
- Never rely on the 100-deep collision suffix; treat >3 collisions as a caller bug.
When it happens
Trigger: A loop or cron job re-publishing the identical topic slug with the same save_suffix and date more than 100 times in one day; an automation bug calling _save_output in a tight loop; a hostile/accidental pre-creation of all candidate names.
Common situations: A retry storm in a hosted batch renderer; scheduled runs that regenerate the same topic hourly; someone seeded the directory with blocking filenames. The code comment itself notes it is 'extremely unlikely' in normal use.
Related errors
- save_output: could not find a unique filename after 101 atte
- [last30days] Cannot read --synthesis-file: {exc}\n
- [CompetitorsPlan] Cannot read plan file: {exc}\n
- Could not find a unique discovery output filename
- [Planner] Cannot read --plan file: {exc}\n
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/f9a110d82ef76ea9.
Report an issue: GitHub.