astrid-runtime/astrid · error
failed to persist {}: {e}
Error message
failed to persist {}: {e} What it means
Thrown by `write_lock` when the atomic persist of Distro.lock fails. The lock file is staged in a `tempfile::NamedTempFile` next to the target and then `tmp.persist(path)` moves it into place; persist can fail (e.g. the target already exists via PersistError, or cross-device rename). The message includes the target path and the underlying {e}.
Source
Thrown at crates/astrid-cli/src/commands/distro/lock.rs:92
};
let lock: DistroLock = toml::from_str(&content).context("failed to parse Distro.lock")?;
Ok(Some(lock))
}
/// Write a lockfile to disk. Uses atomic write (temp + rename) to avoid
/// partial writes on crash.
pub(crate) fn write_lock(path: &Path, lock: &DistroLock) -> anyhow::Result<()> {
let content = toml::to_string_pretty(lock).context("failed to serialize Distro.lock")?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("failed to create {}", parent.display()))?;
}
let mut tmp = tempfile::NamedTempFile::new_in(path.parent().unwrap_or(Path::new(".")))
.context("failed to create temp file for Distro.lock")?;
std::io::Write::write_all(&mut tmp, content.as_bytes())
.context("failed to write Distro.lock staging")?;
tmp.persist(path)
.map_err(|e| anyhow::anyhow!("failed to persist {}: {e}", path.display()))?;
Ok(())
}
/// Convert a parsed lock payload to the bounded daemon control-plane shape.
pub(crate) fn to_provenance(lock: &DistroLock) -> DistroProvenance {
DistroProvenance {
schema_version: lock.schema_version,
distro_id: lock.distro.id.clone(),
distro_version: lock.distro.version.clone(),
resolved_at: lock.distro.resolved_at.clone(),
capsules: lock
.capsules
.iter()
.map(|capsule| DistroCapsuleProvenance {
name: capsule.name.clone(),
version: capsule.version.clone(),
source: capsule.source.clone(),
hash: capsule.hash.clone(),View on GitHub (pinned to affd8760f4)
Solutions
- Check the wrapped {e} for the concrete persist error and fix that condition (permissions, existing file, full disk).
- Re-run the lock generation after stopping concurrent `astrid distro` operations to avoid persist races.
- Ensure the directory containing Distro.lock is writable: `chmod u+w <distro_dir>`.
- If the target exists and is stale, remove `Distro.lock` and regenerate it.
Example fix
// before
let path = Path::new("Distro.lock");
write_lock(path, &content)?; // temp may land in "." on different fs
// after
let path = Path::new("/home/u/proj/Distro.lock");
std::fs::create_dir_all(path.parent().unwrap())?;
write_lock(path, &content)?; Defensive patterns
Strategy: try-catch
Validate before calling
let dir = path.parent().unwrap_or(Path::new("."));
let meta = std::fs::metadata(dir)
.map_err(|e| anyhow!("lock target dir {} unusable: {e}", dir.display()))?;
if meta.permissions().readonly() {
return Err(anyhow!("lock target dir {} is read-only", dir.display()));
} Try / catch
match write_lock(path, &content) {
Err(e) if e.to_string().starts_with("failed to persist") => {
// persist raced or fs issue: retry once after removing a stale target
let _ = std::fs::remove_file(path);
write_lock(path, &content)
}
other => other,
} Prevention
- Ensure the directory containing Distro.lock is writable by the invoking user.
- Don't run concurrent distro lock regenerations for the same workspace.
- Keep temp and target on the same filesystem (don't set TMPDIR cross-device).
- Version-control or back up Distro.lock so a failed persist is recoverable.
When it happens
Trigger: Calling write_lock (directly or via persist_lock_if_earned after a lock-regeneration flow) where persist fails: target file locked/replaced concurrently, target directory on a different filesystem than the temp file, or permission problems moving onto the destination.
Common situations: Two processes regenerating Distro.lock simultaneously and racing persist; the distro directory mounted read-only; tempfile created in `.` fallback (different fs from target) causing cross-device rename errors.
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
- distro source '{source}' must use @owner/repo, a URL, a loca
- --offline: '{source}' is not a local file and network fetch
- Distro.toml has no parent directory
- astrid init requires --distro <@owner/repo, URL, local Distr
- astrid distro apply requires an explicit distro source: @own
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/28ced88431257c19.
Report an issue: GitHub.