astrid-runtime/astrid · error

invalid or duplicate executable name

Error message

invalid or duplicate executable name '{name}'

What it means

Each name in `names` must be exactly one `Normal` path component — no leading `/`, no `..`, no nested paths like `bin/app` — and must appear only once in the set. `validate_replacement_inputs` rejects anything else (or a duplicate) with `InvalidInput`. This guards against path traversal and ambiguous double-installation of the same executable.

Solutions

  1. Pass bare file names only (e.g. `"astrid"`), and normalize any archive-relative paths with `Path::file_name()` before building the list
  2. Deduplicate the list (e.g. via `HashSet` or `dedup()` after sorting) before calling
  3. Reject or skip entries containing separators, `..`, or that are empty, at list-construction time

Example fix

// before
let names: Vec<&str> = entries.iter().map(|e| e.path.as_str()).collect(); // "bin/astrid"
// after
let names: Vec<&str> = entries.iter()
    .filter_map(|e| Path::new(e.path).file_name()?.to_str())
    .collect::<HashSet<_>>().into_iter().collect();
Defensive patterns

Strategy: validation

Validate before calling

fn sanitize_name(name: &str) -> Option<&str> {
    let p = std::path::Path::new(name);
    if p.components().count() == 1 && matches!(p.components().next(), Some(std::path::Component::Normal(_))) {
        Some(name)
    } else {
        None
    }
}
let names: Vec<&str> = raw.into_iter().filter_map(sanitize_name).collect::<std::collections::HashSet<_>>().into_iter().collect();

Type guard

fn is_plain_file_name(name: &str) -> bool {
    !name.is_empty() && std::path::Path::new(name).file_name() == Some(std::ffi::OsStr::new(name))
}

Try / catch

match replace_executable_set(&install_dir, &extract_dir, &names) {
    Err(e) if e.to_string().contains("invalid or duplicate executable name") => {
        eprintln!("malformed executable list: {e}");
    }
    other => other.map_err(Into::into),
}

Prevention

When it happens

Trigger: Passing `"bin/astrid"`, `"../astrid"`, `"/usr/bin/astrid"`, `"./astrid"`, `""`, or the same name twice in the `names` slice to `replace_executable_set`.

Common situations: Deriving names from archive paths that include subdirectories instead of stripping to the file name; a config listing the same executable twice; user-edited manifests containing absolute or relative paths; string joins producing empty entries.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

            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}'"),
            ));
        }

        let source = extract_dir.join(name);
        let metadata = std::fs::symlink_metadata(&source).map_err(|error| {
            io::Error::new(
                error.kind(),
                format!("release archive is missing '{name}': {error}"),
            )
        })?;
        if metadata.file_type().is_symlink() || !metadata.is_file() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("release executable is redirected or not regular: {name}"),
            ));
        }

View on GitHub (pinned to affd8760f4)