astrid-runtime/astrid · error

executable replacement directories must exist

Error message

executable replacement directories must exist

What it means

`validate_replacement_inputs` requires both `install_dir` and `extract_dir` to be existing directories. If either `std::path::Path::is_dir()` returns false — path missing, is a file, or is unreachable — the call fails with `InvalidInput` before any staging or renaming occurs.

Solutions

  1. Verify both paths with `Path::is_dir()` before calling and create `extract_dir` via extraction or `create_dir_all` if missing
  2. Confirm the configured install directory actually exists and contains the executables being replaced
  3. Re-run the archive extraction step and confirm it succeeded before attempting replacement

Example fix

// before
replace_executable_set(&install_dir, &extract_dir, &names)?;
// after
if !install_dir.is_dir() {
    std::fs::create_dir_all(&install_dir)?;
}
if !extract_dir.is_dir() {
    return Err(anyhow!("extract dir missing: {}", extract_dir.display()));
}
replace_executable_set(&install_dir, &extract_dir, &names)?;
Defensive patterns

Strategy: validation

Validate before calling

if !install_dir.is_dir() {
    std::fs::create_dir_all(install_dir)?;
}
if !extract_dir.is_dir() {
    return Err(anyhow!("extract dir missing: {}", extract_dir.display()));
}

Type guard

fn both_dirs_exist(a: &Path, b: &Path) -> bool {
    a.is_dir() && b.is_dir()
}

Try / catch

match replace_executable_set(&install_dir, &extract_dir, names) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        eprintln!("check install_dir/extract_dir exist as directories: {e}");
    }
    other => other.map_err(Into::into),
}

Prevention

When it happens

Trigger: Calling `replace_executable_set` with an `extract_dir` that was never created (archive extraction skipped or failed), an `install_dir` typo, or either path pointing at a file instead of a directory; also when the path was deleted by another process between planning and the call.

Common situations: Extraction to a temp dir failed silently earlier and the temp dir was cleaned up; wrong env var/config key supplying the install directory; running the updater before the first install created the install dir; symlinks whose targets are gone.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/a83ed7c089585403. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-core/src/platform_fs.rs:832

    #[cfg(not(windows))]
    {
        replace_executable_set_by_rename(install_dir, extract_dir, names)
    }
}

fn validate_replacement_inputs(
    install_dir: &Path,
    extract_dir: &Path,
    names: &[&str],
) -> io::Result<()> {
    if names.is_empty() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "executable replacement set must not be empty",
        ));
    }
    if !install_dir.is_dir() || !extract_dir.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "executable replacement directories must exist",
        ));
    }

    let mut unique = HashSet::with_capacity(names.len());
    for name in names {
        let mut components = Path::new(name).components();
        if !matches!(components.next(), Some(Component::Normal(_)))
            || components.next().is_some()
            || !unique.insert(*name)
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("invalid or duplicate executable name '{name}'"),
            ));
        }

View on GitHub (pinned to affd8760f4)