zed-industries/zed · error · RuntimeError
build directory already exists but is not ready; refusing to
Error message
build directory already exists but is not ready; refusing to overwrite {build_id} What it means
Raised as RuntimeError by modal_app.build_eval_cli() at the fast-path check: the build directory /data/builds/{build_id} exists on the Modal volume, but the READY marker and/or the eval-cli binary are missing, so the build is not reusable and the function refuses to overwrite it. This guard protects a half-written or corrupted build from being silently replaced while other runs may reference it.
Source
Thrown at crates/eval_cli/zed_eval/modal_app.py:185
build_info_path = build_dir / "build-info.json"
# The lease lives outside build_dir so it never interferes with the atomic
# move of the finished build directory below.
building_path = pathlib.Path("/data/build-locks") / f"{build_id}.json"
lease_ttl = int(build_request.get("build_wait_timeout_secs") or 7200)
owner = uuid.uuid4().hex
def reuse_existing() -> dict[str, Any]:
build_info = load_json(build_info_path) or {}
build_info.setdefault("build_id", build_id)
build_info["reused"] = True
volume.commit()
print(f"Reusing existing build {build_id}", flush=True)
return build_info
if ready_path.exists() and binary_path.exists():
return reuse_existing()
if build_dir.exists():
raise RuntimeError(
f"build directory already exists but is not ready; refusing to overwrite {build_id}"
)
# Single-flight lease: if another invocation is already compiling this exact
# build, wait for it to finish rather than running a second multi-minute
# compile. The lease is best-effort (the volume has no atomic compare-and-swap);
# the atomic move below still guarantees correctness if two builds slip through.
deadline = time.time() + lease_ttl
while True:
reload_volume()
if ready_path.exists() and binary_path.exists():
return reuse_existing()
if build_dir.exists():
raise RuntimeError(
f"build directory already exists but is not ready; refusing to overwrite {build_id}"
)
lease = load_json(building_path)
now = time.time()View on GitHub (pinned to bc538def45)
Solutions
- Inspect the directory on the volume (Modal shell or volume tooling): ls /data/builds/<build_id> — check for eval-cli, build-info.json, READY.
- If READY is missing but eval-cli and build-info.json are intact and trustworthy, restore the READY marker; otherwise delete the whole directory.
- Delete the stale directory and re-run zed-eval build (or the launch) so the build is recreated atomically.
- Also clear a leftover lease file /data/build-locks/<build_id>.json if present, so the single-flight wait does not block on a dead owner.
Example fix
# recovery, from a Modal shell on the volume # before ls /data/builds/my-build-abc # dir exists, no READY -> RuntimeError # after rm -rf /data/builds/my-build-abc /data/build-locks/my-build-abc.json volume.commit() # then re-run zed-eval build
Defensive patterns
Strategy: retry
Validate before calling
# Before re-running a build whose previous attempt died: # (from a Modal shell with the volume mounted) # ls /data/builds/<build_id> -> expect eval-cli, build-info.json, READY # If READY is missing, the dir is incomplete and must be removed.
Try / catch
try:
build_info = build_function.remote(build_request)
except RuntimeError as error:
if "refusing to overwrite" not in str(error):
raise
# inspect /data/builds/<build_id> on the volume; delete the incomplete
# directory (and /data/build-locks/<build_id>.json), commit, retry once Prevention
- Check for a reusable build (READY + eval-cli present) before re-launching the same source.
- Never manually create or partially delete files under /data/builds/<id>.
- After any killed build, clean both the build dir and the lease file so retries start clean.
When it happens
Trigger: A previous build of the same id crashed after creating build_dir but before completing (older/non-atomic code paths, a Modal container killed mid-write); someone manually created or partially deleted files under /data/builds/{id}; volume data loss or partial replication leaving the directory without READY; a stale directory left by an interrupted cleanup.
Common situations: Retrying a launch after a build timeout where the first attempt died mid-compile before the atomic move; ops scripts touching /data/builds; running two infrastructures (e.g. renamed app/volume) against the same build ids with different layouts.
Related errors
- build {build_id} was not ready before the wait timeout
- eval-cli binary not found at {binary}. Build it with: cargo
- build ids may only contain lowercase letters, numbers, '.',
- app '{args.app_name}' not deployed (or function '{function_n
- zed benchmarks require build_id
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/22e5129a75b1ddda.
Report an issue: GitHub.