astrid-runtime/astrid · critical

{detail}: {error}

Error message

{detail}: {error}

What it means

After staging, the installer renames each `.{name}.new` onto its live path. If a rename fails, the function rolls back already-installed files from `.bak` copies; this error wraps both the rename failure and the final detail message. If rollback also failed, the detail reads `failed to install <live>; rollback also failed (...); restore *.bak manually` — the installation may be in a mixed state and the user must restore backups by hand.

Solutions

  1. Stop all running instances of the executable (or update a copy not currently executing) and retry the update
  2. Manually restore each `<name>.bak` over `<name>` in `install_dir` as the message instructs, then retry the update
  3. Check and remove immutable flags / policy blocks (`chattr -i`, SELinux/AppArmor) on install_dir
  4. Serialize updater runs with a lock file to avoid concurrent replacement attempts

Example fix

// before
app.update_self()?; // app is currently running -> ETXTBSY
// after
if !current_exe_is_running_copy() {
    app.update_self()?;
} else {
    spawn_detached_updater_and_exit();
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn live_executables_idle(install_dir: &Path, names: &[&str]) -> bool {
    !install_dir.join(".running.lock").exists()
        && names.iter().all(|n| is_replaceable(&install_dir.join(n)))
}
fn is_replaceable(p: &Path) -> bool {
    !p.exists() || std::fs::rename(p, p).is_ok()
}

Try / catch

match replace_executable_set(&install_dir, &extract_dir, names) {
    Err(e) if e.to_string().contains("rollback also failed") => {
        eprintln!("MANUAL ACTION REQUIRED: restore <name>.bak over <name> in {}", install_dir.display());
        std::process::exit(1);
    }
    Err(e) => return Err(e.into()),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: `std::fs::rename(temporary, live)` fails — typically ETXTBSY (live executable still running on Linux), EACCES/EPERM (no permission to replace in install_dir), or the live target is held/immutable; rollback then also fails when `.bak` copies are missing, locked, or the directory became read-only.

Common situations: Self-updating a running CLI (ETXTBSY on Linux); another updater instance running concurrently; SELinux/AppArmor or read-only mounts blocking the rename; `.bak` files deleted by cleanup tools so rollback cannot restore.

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/6c3a2c068a985428. Report an issue: GitHub.

Appendix: source

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

                            .push(format!("{}: {rollback_error}", installed_live.display()));
                    }
                } else if let Err(rollback_error) = std::fs::remove_file(installed_live) {
                    rollback_errors.push(format!("{}: {rollback_error}", installed_live.display()));
                }
            }
            for (remaining, _) in &staged[index..] {
                let _ = std::fs::remove_file(remaining);
            }
            let detail = if rollback_errors.is_empty() {
                format!("failed to install {}", live.display())
            } else {
                format!(
                    "failed to install {}; rollback also failed ({}); restore *.bak manually",
                    live.display(),
                    rollback_errors.join("; ")
                )
            };
            return Err(io::Error::new(error.kind(), format!("{detail}: {error}")));
        }
    }
    Ok(())
}

#[cfg(test)]
#[path = "platform_fs/tests.rs"]
mod tests;

View on GitHub (pinned to affd8760f4)