headroomlabs-ai/headroom · error · SystemExit
output directory must be empty to avoid stale artifacts: {pa
Error message
output directory must be empty to avoid stale artifacts: {path} What it means
Same guard as the Python smoke's ensure_empty_dir(), but in the top-level orchestrator scripts/release_smoke_all.py: it creates the run's output directory and refuses to continue if it is non-empty, so a run's summary never mixes artifacts from different runs.
Source
Thrown at scripts/release_smoke_all.py:50
return tomllib.load(fh)["project"]["version"]
def quote_arg(value: str | os.PathLike[str]) -> str:
text = str(value)
if re.fullmatch(r"[A-Za-z0-9_./:=\\-]+", text):
return text
return f'"{text.replace(chr(34), chr(34) * 2)}"'
def run(args: list[str | os.PathLike[str]]) -> None:
print("\n> " + " ".join(quote_arg(arg) for arg in args), flush=True)
subprocess.run([str(arg) for arg in args], cwd=ROOT, check=True)
def ensure_empty_dir(path: Path) -> None:
path.mkdir(parents=True, exist_ok=True)
if any(path.iterdir()):
raise SystemExit(f"output directory must be empty to avoid stale artifacts: {path}")
def parse_args() -> argparse.Namespace:
version = load_project_version()
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
default_out = ROOT / "release-assets-local" / f"all-{version}-{stamp}"
parser = argparse.ArgumentParser(description="Run local npm and Python release smokes.")
parser.add_argument("--out", type=Path, default=default_out)
parser.add_argument("--python", default=sys.executable)
parser.add_argument("--node", default=shutil.which("node") or "node")
parser.add_argument(
"--python-release",
action="store_true",
help="Run the Python smoke with maturin --release instead of the faster ci profile.",
)
parser.add_argument("--skip-npm", action="store_true")
parser.add_argument("--skip-python", action="store_true")View on GitHub (pinned to 322425c43b)
Solutions
- Drop `--out` and let the script use its timestamped default directory.
- Or remove/rename the existing directory before rerunning: `rm -rf <out_dir>`.
- In CI, use a unique directory per attempt (`out-$GITHUB_RUN_ATTEMPT`) instead of a fixed path.
Example fix
# before python scripts/release_smoke_all.py --out ./release-out # second run fails # after python scripts/release_smoke_all.py # default: release-assets-local/all-<version>-<stamp>
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
import sys
def ensure_runnable(out_dir: Path, skip_npm: bool, skip_python: bool) -> None:
if skip_npm and skip_python:
raise SystemExit("nothing to run: at least one smoke must be enabled")
out_dir.mkdir(parents=True, exist_ok=True)
if any(out_dir.iterdir()):
raise SystemExit(f"stale output dir: {out_dir}") Prevention
- Prefer the script's timestamped default --out over a fixed path.
- Validate flag combinations at the top of wrapper scripts before delegating.
- In CI, use run-attempt-unique output directories.
When it happens
Trigger: Passing `--out <dir>` that already contains files. The default output path embeds version + a `%Y%m%d-%H%M%S` timestamp, so the default essentially never collides — hitting this error almost always means an explicit `--out` was reused.
Common situations: Scripting repeated smokes with a fixed `--out ./out`; CI caching the output directory between attempts; a prior run crashing after writing partial results.
Related errors
- output directory must be empty to avoid stale artifacts: {pa
- nothing to run: --skip-npm and --skip-python were both set
- failed to read simulator config {path}: {source}
- binary cache directory parent is not writable: {dest.parent}
- binary cache directory is not writable: {dest.parent}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/838d95322d67774c.
Report an issue: GitHub.