astrid-runtime/astrid · error
legacy distro conflict at {path}: {detail}
Error message
legacy distro conflict at {path}: {detail} What it means
This io::Error with ErrorKind::AlreadyExists signals a conflict during legacy distro migration: something the migration wants to create or retire already exists in an incompatible state at {path}. The {detail} describes the colliding artifact (e.g. an existing lock or destination).
Source
Thrown at crates/astrid-kernel/src/principal_distro_migration.rs:569
}
#[cfg(not(unix))]
// Windows publication is already write-through; keep the shared fallible
// signature so migration call sites remain platform-independent.
#[allow(clippy::unnecessary_wraps)]
fn sync_parent(_path: &Path) -> io::Result<()> {
Ok(())
}
fn invalid(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::InvalidData,
format!("legacy distro {}: {detail}", path.display()),
)
}
fn conflict(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::AlreadyExists,
format!("legacy distro conflict at {}: {detail}", path.display()),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn lock_text() -> &'static str {
"schema-version = 1\n\n[distro]\nid = \"example\"\nversion = \"1.0.0\"\nresolved-at = \"2026-01-01T00:00:00Z\"\n"
}
#[test]
fn source_snapshot_counts_only_the_private_lock() {
let directory = tempfile::tempdir().unwrap();
let home = AstridHome::from_path(directory.path().join("astrid"));
let alias = PrincipalId::new("alice").unwrap();View on GitHub (pinned to affd8760f4)
Solutions
- Check whether a previous migration run partially completed and clean up stale locks at {path}
- Ensure only one migration process runs at a time
- Remove or rename the conflicting artifact once its state is understood
- Re-run the migration, which should now proceed idempotently
Example fix
// before: hard failure on existing lock
retire_legacy_distro_init_locks(path)?;
// after: tolerate already-retired locks
match retire_legacy_distro_init_locks(path) {
Ok(()) => {},
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => log::info!("locks already retired"),
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
if init_lock_path.exists() { cleanup_stale_lock(init_lock_path)?; } Type guard
fn lock_is_stale(p: &Path, max_age: Duration) -> bool {
p.metadata().map(|m| m.modified().map(|t| t.elapsed().unwrap_or_default() > max_age).unwrap_or(true)).unwrap_or(false)
} Try / catch
match migrate_one(path) {
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => cleanup_and_retry(path)?,
Err(e) => return Err(e),
Ok(()) => {},
} Prevention
- Use an external process lock so only one migration runs at a time
- Clean stale init locks after crashed runs
- Make migration idempotent so re-runs tolerate existing artifacts
When it happens
Trigger: Calling conflict(path, detail) from retire_legacy_distro_init_locks, migrate_one, retire_source, read_digest, or read_bounded when an init lock, destination entry, or source already exists or is unexpectedly present.
Common situations: Re-running a partially completed migration, concurrent migration processes racing on the same distro, stale lock files left by a crashed prior run.
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
- legacy and operator REPL histories differ; refusing to merge
- legacy audit retirement is ambiguous: {}
- legacy log conflict at {}: {detail}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/9dfd420e9309f511.
Report an issue: GitHub.