{"record":{"id":"28ced88431257c19","repo":"astrid-runtime/astrid","slug":"failed-to-persist-e-28ced8","errorCode":null,"errorMessage":"failed to persist {}: {e}","messagePattern":"failed to persist (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/distro/lock.rs","lineNumber":92,"sourceCode":"    };\n    let lock: DistroLock = toml::from_str(&content).context(\"failed to parse Distro.lock\")?;\n    Ok(Some(lock))\n}\n\n/// Write a lockfile to disk. Uses atomic write (temp + rename) to avoid\n/// partial writes on crash.\npub(crate) fn write_lock(path: &Path, lock: &DistroLock) -> anyhow::Result<()> {\n    let content = toml::to_string_pretty(lock).context(\"failed to serialize Distro.lock\")?;\n    if let Some(parent) = path.parent() {\n        std::fs::create_dir_all(parent)\n            .with_context(|| format!(\"failed to create {}\", parent.display()))?;\n    }\n    let mut tmp = tempfile::NamedTempFile::new_in(path.parent().unwrap_or(Path::new(\".\")))\n        .context(\"failed to create temp file for Distro.lock\")?;\n    std::io::Write::write_all(&mut tmp, content.as_bytes())\n        .context(\"failed to write Distro.lock staging\")?;\n    tmp.persist(path)\n        .map_err(|e| anyhow::anyhow!(\"failed to persist {}: {e}\", path.display()))?;\n    Ok(())\n}\n\n/// Convert a parsed lock payload to the bounded daemon control-plane shape.\npub(crate) fn to_provenance(lock: &DistroLock) -> DistroProvenance {\n    DistroProvenance {\n        schema_version: lock.schema_version,\n        distro_id: lock.distro.id.clone(),\n        distro_version: lock.distro.version.clone(),\n        resolved_at: lock.distro.resolved_at.clone(),\n        capsules: lock\n            .capsules\n            .iter()\n            .map(|capsule| DistroCapsuleProvenance {\n                name: capsule.name.clone(),\n                version: capsule.version.clone(),\n                source: capsule.source.clone(),\n                hash: capsule.hash.clone(),","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/lock.rs#L74-L110","documentation":"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}.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet path = Path::new(\"Distro.lock\");\nwrite_lock(path, &content)?; // temp may land in \".\" on different fs\n// after\nlet path = Path::new(\"/home/u/proj/Distro.lock\");\nstd::fs::create_dir_all(path.parent().unwrap())?;\nwrite_lock(path, &content)?;","handlingStrategy":"try-catch","validationCode":"let dir = path.parent().unwrap_or(Path::new(\".\"));\nlet meta = std::fs::metadata(dir)\n    .map_err(|e| anyhow!(\"lock target dir {} unusable: {e}\", dir.display()))?;\nif meta.permissions().readonly() {\n    return Err(anyhow!(\"lock target dir {} is read-only\", dir.display()));\n}","typeGuard":null,"tryCatchPattern":"match write_lock(path, &content) {\n    Err(e) if e.to_string().starts_with(\"failed to persist\") => {\n        // persist raced or fs issue: retry once after removing a stale target\n        let _ = std::fs::remove_file(path);\n        write_lock(path, &content)\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["distro","file-write","atomic-write","cli"],"backgroundTag":"file-write-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}