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

  1. Inspect the directory on the volume (Modal shell or volume tooling): ls /data/builds/<build_id> — check for eval-cli, build-info.json, READY.
  2. If READY is missing but eval-cli and build-info.json are intact and trustworthy, restore the READY marker; otherwise delete the whole directory.
  3. Delete the stale directory and re-run zed-eval build (or the launch) so the build is recreated atomically.
  4. 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

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


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/22e5129a75b1ddda. Report an issue: GitHub.