sinelaw/fresh · error · UpdateError

AlreadyExists

AlreadyExists

Error message

could not create a private staging file next to the target

What it means

`staging_file` retries `N` times to `create` a new exclusive staging file next to the target; if every attempt collides with an existing file, it gives up with `io::ErrorKind::AlreadyExists`. This should be near-impossible and usually means the filesystem is misbehaving or the naming scheme collides persistently.

Solutions

  1. Inspect the target directory and delete stale `.tmp` staging files left by crashed updates
  2. Check filesystem type/mount options support exclusive creation (O_EXCL)
  3. Increase the retry count or use random/uuid-based staging names to avoid collisions
  4. Ensure the update process has write permission to the target directory

Example fix

// before
let name = format!("{}.tmp-{}", target_name, i);
// after
let name = format!("{}.tmp-{}-{}", target_name, std::process::id(), uuid::Uuid::new_v4().simple());
Defensive patterns

Strategy: retry

Validate before calling

// preflight: check writability and clean stale staging files
for entry in std::fs::read_dir(target.parent().unwrap())? {
    let p = entry?.path();
    if p.to_string_lossy().contains(".tmp") && is_stale(&p) { let _ = std::fs::remove_file(p); }
}

Try / catch

match staging_file(&target) {
    Err(UpdateError::Io(e)) if e.kind() == io::ErrorKind::AlreadyExists => {
        // fall back to temp-dir staging or abort with a clear user message
    }
    other => other?,
}

Prevention

When it happens

Trigger: All attempted staging filenames (e.g. `.target.tmp-N`) already exist and `create_exclusive` keeps failing with AlreadyExists across the retry loop; the target directory contains many stale staging files; a filesystem that does not honor O_EXCL.

Common situations: Read-only or weird-FS mounts where create behaves unexpectedly; leftover staging files from crashed updates exhausting the name space; a malicious actor pre-creating predictable staging names next to a setuid binary.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/d1c40a30d9c9d495. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-update/src/self_update.rs:139

    let name = target
        .file_name()
        .map(|n| n.to_string_lossy().into_owned())
        .unwrap_or_else(|| "fresh".to_string());
    let dir = target.parent().unwrap_or_else(|| Path::new("."));
    for attempt in 0..16u32 {
        let nonce = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.subsec_nanos())
            .unwrap_or(0)
            ^ attempt.wrapping_mul(0x9E37_79B9);
        let path = dir.join(format!(".{name}.new-{nonce:08x}"));
        match create_exclusive(&path) {
            Ok(file) => return Ok((path, file)),
            Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
            Err(e) => return Err(UpdateError::Io(e)),
        }
    }
    Err(UpdateError::Io(std::io::Error::new(
        std::io::ErrorKind::AlreadyExists,
        "could not create a private staging file next to the target",
    )))
}

#[cfg(unix)]
fn create_exclusive(path: &Path) -> std::io::Result<std::fs::File> {
    use std::os::unix::fs::OpenOptionsExt;
    std::fs::OpenOptions::new()
        .write(true)
        .create_new(true)
        .custom_flags(libc::O_NOFOLLOW)
        .mode(0o600)
        .open(path)
}

#[cfg(not(unix))]
fn create_exclusive(path: &Path) -> std::io::Result<std::fs::File> {

View on GitHub (pinned to 67894ca546)