astrid-runtime/astrid · error

failed to stage {}: {error}

Error message

failed to stage {}: {error}

What it means

During `replace_executable_set_by_rename`, each new executable is staged as `.{name}.new` inside `install_dir` (copied from `extract_dir`, chmod 0o755 on Unix). If any staging step fails, previously staged temporaries are cleaned up and the original OS error is re-raised with the temporary path prefixed: `failed to stage <path>: <error>`. Nothing live has been replaced yet, so the installation is untouched.

Source

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

    let mut staged = Vec::new();
    for name in names {
        let temporary = install_dir.join(format!(".{name}.new"));
        let stage_result = (|| -> io::Result<()> {
            std::fs::copy(extract_dir.join(name), &temporary)?;
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt as _;
                std::fs::set_permissions(&temporary, std::fs::Permissions::from_mode(0o755))?;
            }
            Ok(())
        })();
        if let Err(error) = stage_result {
            let _ = std::fs::remove_file(&temporary);
            for (staged_temporary, _) in &staged {
                let _ = std::fs::remove_file(staged_temporary);
            }
            return Err(io::Error::new(
                error.kind(),
                format!("failed to stage {}: {error}", temporary.display()),
            ));
        }
        staged.push((temporary, install_dir.join(name)));
    }

    for (index, (temporary, live)) in staged.iter().enumerate() {
        if let Err(error) = std::fs::rename(temporary, live) {
            let mut rollback_errors = Vec::new();
            for (_, installed_live) in &staged[..index] {
                if let Some((_, backup)) = backups
                    .iter()
                    .find(|(backup_live, _)| backup_live == installed_live)
                {
                    if let Err(rollback_error) = std::fs::rename(backup, installed_live) {
                        rollback_errors
                            .push(format!("{}: {rollback_error}", installed_live.display()));

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check disk space (`df -h`) and free space on the install volume, then retry
  2. Ensure the process has write permission to `install_dir` (run with sufficient privileges or install to a user-writable location)
  3. Confirm the source file in `extract_dir` still exists and is readable at staging time; re-extract if it vanished
  4. Exclude `install_dir` from antivirus/backup interference or close handles holding the `.new` file

Example fix

// before
replace_executable_set(&Path::new("/usr/local/bin"), &extract_dir, names)?; // EACCES
// after
let install_dir = user_writable_bin_dir(); // e.g. ~/.local/bin
replace_executable_set(&install_dir, &extract_dir, names)?;
Defensive patterns

Strategy: try-catch

Validate before calling

if !install_dir.is_dir() {
    return Err(anyhow!("install dir missing"));
}
if std::fs::metadata(install_dir)?.permissions().readonly() {
    return Err(anyhow!("install dir is read-only"));
}
if fs4::available_space(install_dir).unwrap_or(0) < min_required_bytes {
    return Err(anyhow!("insufficient disk space"));
}

Try / catch

match replace_executable_set(&install_dir, &extract_dir, names) {
    Err(e) => {
        match e.raw_os_error() {
            Some(libc::ENOSPC) => eprintln!("disk full; free space and retry"),
            Some(libc::EACCES) | Some(libc::EPERM) => eprintln!("need write permission on install dir"),
            _ => eprintln!("staging failed: {e}"),
        }
    }
    Ok(()) => {},
}

Prevention

When it happens

Trigger: `std::fs::copy(extract_dir/name, install_dir/.name.new)` fails — typically ENOSPC (disk full on install_dir's volume), EACCES (no write permission in install_dir), or the extract source disappeared between validation and staging; `set_permissions(0o755)` failing for the same permission reasons.

Common situations: Install directory on a full or read-only volume (e.g. `/usr/local/bin` requiring root); antivirus or backup software locking/removing the temp file mid-copy; running the updater without elevated privileges after installing system-wide.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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