Kuberwastaken/claurst · error

installed binary path has no file name

Error message

installed binary path has no file name: {}

What it means

create_next_to called Path::file_name() on the installed binary path and got None, meaning the path terminates in '..' or is the filesystem root. The upgrader needs the original file name to name the staged replacement binary. This is essentially an invariant violation: an executable path should always have a final component.

Solutions

  1. Canonicalize the binary path before staging so '..' components are resolved away.
  2. Reinstall the binary at a normal absolute path with a real file name.
  3. Inspect how the binary is being launched (wrapper scripts, symlinks) and fix the path.
  4. Fall back to installing to a default bin directory if the current path is unusable.

Example fix

// before
let current = std::env::current_exe()?;
// after
let current = std::env::current_exe()?.canonicalize()?;
Defensive patterns

Strategy: validation

Validate before calling

// canonicalize to eliminate '..' and root-only paths
let current = std::env::current_exe()?.canonicalize()?;
if current.file_name().is_none() {
    bail!("cannot determine binary file name from {}", current.display());
}

Prevention

When it happens

Trigger: The resolved current-exe path ends with '..' or is root ('/'), so file_name() returns None in create_next_to during run_upgrade's staging step.

Common situations: Symlink chains or canonicalization producing a trailing '..' component; binary path set to '/' via misconfiguration; exotic container/sandbox mount layouts; manually hacked install location.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/4cc30187eb119b63. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/cli/src/upgrade.rs:316

    path: PathBuf,
}

#[cfg(unix)]
impl StagedBinary {
    fn create_next_to(current: &Path) -> Result<(Self, std::fs::File)> {
        use std::ffi::OsString;
        use std::fs::OpenOptions;
        use std::io::ErrorKind;
        use std::sync::atomic::{AtomicU64, Ordering};

        static NEXT_STAGE_ID: AtomicU64 = AtomicU64::new(0);
        const MAX_ATTEMPTS: usize = 128;

        let parent = current
            .parent()
            .ok_or_else(|| anyhow!("installed binary path has no parent: {}", current.display()))?;
        let file_name = current.file_name().ok_or_else(|| {
            anyhow!(
                "installed binary path has no file name: {}",
                current.display()
            )
        })?;

        for _ in 0..MAX_ATTEMPTS {
            let id = NEXT_STAGE_ID.fetch_add(1, Ordering::Relaxed);
            let mut staged_name = OsString::from(".");
            staged_name.push(file_name);
            staged_name.push(format!(".upgrade-{}-{}", std::process::id(), id));
            let path = parent.join(staged_name);

            match OpenOptions::new().write(true).create_new(true).open(&path) {
                Ok(file) => return Ok((Self { path }, file)),
                Err(error) if error.kind() == ErrorKind::AlreadyExists => continue,
                Err(error) => {
                    return Err(error).with_context(|| {
                        format!(

View on GitHub (pinned to b0637c97ec)