Kuberwastaken/claurst · error

installed binary path has no parent

Error message

installed binary path has no parent: {}

What it means

In create_next_to, Path::parent() on the currently installed binary returned None, which happens only for a root-relative bare path like "/bin" or a bare file name with no directory component. The upgrade routine needs the parent directory to stage the new binary next to the old one.

Solutions

  1. Install the binary in a normal directory (e.g. ~/.local/bin, /usr/local/bin) so it has a resolvable parent.
  2. Check where the running binary lives (which claurst; readlink /proc/self/exe) and reinstall there properly.
  3. Pass an explicit target/install path if the tool supports it.
  4. Avoid running the binary directly from "/" or via a bare-name mount.

Example fix

// before
$ sudo cp claurst /           # path with no usable parent for staging
// after
$ sudo cp claurst /usr/local/bin/claurst
Defensive patterns

Strategy: validation

Validate before calling

// validate the install path before upgrading
let path = std::env::current_exe()?;
if path.parent().is_none() {
    bail!("binary must be installed in a real directory, not {}", path.display());
}

Prevention

When it happens

Trigger: current_exe() or the configured binary path resolves to a path with no parent component (e.g. "/" child without intermediate dirs, or a bare relative name when current_dir metadata is odd), in create_next_to during upgrade.

Common situations: Binary copied/moved to an exotic location like filesystem root; running a binary whose path was canonicalized to "/"; custom PATH entry pointing at a bare name; sandboxed environments reporting odd exe paths.

Related errors


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

Appendix: source

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

#[cfg(unix)]
struct StagedBinary {
    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) => {

View on GitHub (pinned to b0637c97ec)