astrid-runtime/astrid · error
quarantine to
Error message
quarantine {} to {}: {error} What it means
This error wraps a failed `fs::rename` while quarantining an unexpected file found in the principal home during migration. The kernel first moves the stray file into a private quarantine directory before continuing; if the OS rename fails, the original OS error kind is preserved and both source and destination paths are embedded in the message. It exists so migration never silently destroys or loses unexplained files.
Solutions
- Re-run the operation; transient locks from sync/backup tools usually clear once those tools are paused
- Exclude the principal home directory from file sync/backup software
- Check that the home and migrations directory are on the same filesystem and writable by the current user
- Inspect the wrapped OS error kind in the message (e.g. EXDEV, EACCES, ENOENT) to target the filesystem/permission cause
Example fix
// before
fs::rename(source, &destination)?;
// after
// fall back to copy+remove when rename fails across devices
match fs::rename(source, &destination) {
Ok(()) => {},
Err(e) if e.kind() == io::ErrorKind::CrossesDevices => {
fs::copy(source, &destination)?;
fs::remove_file(source)?;
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling admission, ensure the home is quiescent and writable
let meta = std::fs::metadata(source)?;
if !meta.is_file() { return Err("unexpected non-file entry"); }
let test = std::fs::OpenOptions::new().append(true).open(source.parent().unwrap())?; Try / catch
match err.downcast_ref::<io::Error>() {
Some(e) if e.kind() == io::ErrorKind::CrossesDevices => /* copy+remove fallback */,
Some(e) if e.kind() == io::ErrorKind::PermissionDenied => /* fix perms, retry */,
_ => /* surface to user */,
} Prevention
- Pause file-sync/backup tools that watch the principal home during migration
- Keep the home on a single filesystem so rename never needs EXDEV fallback
- Run migrations while no other app instance is open
When it happens
Trigger: `quarantine_entry` (via `admit_or_quarantine_entry`) calls `fs::rename(source, destination)` and the OS rejects it: source file deleted/renamed concurrently, destination collision despite `unique_quarantine_path`, cross-device rename, or permission issues on the source or migrations dir.
Common situations: Another process (editor, sync tool like Dropbox/Nextcloud, backup agent) is touching files inside the home while migration runs; the home spans multiple filesystems so rename is EXDEV; the migrations directory was manually created with wrong permissions.
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
- layout migration destination changed type
- layout migration destination changed while inventoried
- layout migration destination is redirected or not a regular…
- layout migration receipt destination path does not match…
- layout migration source is redirected or not a directory
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/8a4a46d452784cb5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/principal_home_migration/unbound.rs:241
io::ErrorKind::InvalidData,
format!("principal {alias} has an invalid genesis public key: {error}"),
)
})?;
Ok(public_key.into())
}
fn quarantine_entry(
home: &AstridHome,
source: &Path,
file_name: &OsStr,
reason: &str,
) -> io::Result<()> {
let quarantine_root = home.migrations_dir().join(QUARANTINE_DIR);
astrid_core::platform_fs::ensure_private_directory(&quarantine_root)?;
let destination = unique_quarantine_path(&quarantine_root, file_name)?;
let source_parent = source.parent().map(Path::to_path_buf);
fs::rename(source, &destination).map_err(|error| {
io::Error::new(
error.kind(),
format!(
"quarantine {} to {}: {error}",
source.display(),
destination.display()
),
)
})?;
if let Some(parent) = source_parent.as_deref() {
sync_directory(parent)?;
}
sync_parent(&destination)?;
let sidecar = destination.with_file_name(format!(
"{}.original-name",
destination
.file_name()
.map_or("leftover", |name| name.to_str().unwrap_or("leftover"))
));View on GitHub (pinned to affd8760f4)