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
- Inspect the target directory and delete stale `.tmp` staging files left by crashed updates
- Check filesystem type/mount options support exclusive creation (O_EXCL)
- Increase the retry count or use random/uuid-based staging names to avoid collisions
- 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
- Use random/uuid staging names to make collisions improbable
- Clean up staging files on crash/exit (drop guards)
- Verify the target directory is writable before updating
- Alert the user if exclusivity repeatedly fails (possible tampering or FS limitations)
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
- Failed to read
- Failed to read plugin
- home directory not found
- Filesystem not available
- read_range: expected
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)