{"record":{"id":"a2df8c0118011e08","repo":"astrid-runtime/astrid","slug":"alreadyexists-a2df8c","errorCode":"AlreadyExists","errorMessage":"a private-file write transaction is already pending","messagePattern":"a private-file write transaction is already pending","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs/windows/private_file.rs","lineNumber":201,"sourceCode":"    Ok(journal)\n}\n\npub(super) fn write_private_file_transaction_journal(\n    parent: &Path,\n    journal: &PrivateFileTransaction,\n    guard: &TrustedPathGuard,\n) -> io::Result<()> {\n    let journal_path = parent.join(PRIVATE_FILE_TRANSACTION_JOURNAL);\n    if guarded_file_exists(guard, &journal_path).map_err(|error| {\n        with_context(\n            error,\n            format!(\n                \"could not inspect private-file journal before publication: {}\",\n                journal_path.display()\n            ),\n        )\n    })? {\n        return Err(io::Error::new(\n            io::ErrorKind::AlreadyExists,\n            \"a private-file write transaction is already pending\",\n        ));\n    }\n    let bytes = serde_json::to_vec(journal)\n        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;\n    let (staged, journal_file) =\n        stage_unique_bytes_retained(guard, parent, &bytes, \"astrid-private-write-journal\")?;\n    #[cfg(test)]\n    if let Err(error) = super::io::test_maybe_fail_journal_rename() {\n        drop(journal_file);\n        let _ = remove_guarded_file(guard, &staged);\n        return Err(error);\n    }\n    if let Err(error) = move_guarded_file(guard, &staged, &journal_path) {\n        drop(journal_file);\n        let _ = remove_guarded_file(guard, &staged);\n        return Err(error);","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs/windows/private_file.rs#L183-L219","documentation":"Before publishing a new journal, write_private_file_transaction_journal checks whether the journal file (.astrid-private-write.transaction.json) already exists. If it does, another private-file write transaction is pending (likely from a crashed or concurrent writer) and starting a new one would corrupt recovery state, so the call fails with AlreadyExists.","triggerScenarios":"A previous atomic_write_private_file crashed between journal publication and cleanup; two processes attempt private-file writes to the same directory concurrently without proper lock coordination; a stale journal survived a crash.","commonSituations":"App crash or power loss mid-write leaving the journal behind; parallel threads/processes writing different private files in the same directory; manually copied directories containing a leftover journal file.","solutions":["Retry the write: the library runs recover_private_file_transaction_locked on each call, which should roll back and remove a stale journal.","Ensure only one process/thread writes private files in a given directory at a time (the transaction lock serializes them; check it was acquired).","Inspect the directory for .astrid-private-write.transaction.json and .astrid-private.* files; if a stale journal persists after recovery, remove the leftover .astrid-private.* staging/rollback files and let a fresh call recover."],"exampleFix":"// before\natomic_write_private_file(&path, data)?; // fails: AlreadyExists\n// after: retry after recovery has cleared the pending journal\nfor attempt in 0..3 {\n    match atomic_write_private_file(&path, data) {\n        Ok(()) => break,\n        Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists && attempt < 2 => continue,\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":"let journal_pending = dir.join(\".astrid-private-write.transaction.json\").exists();\nif journal_pending { /* trigger a recovery pass (any read/write attempt runs recovery) before writing */ }","typeGuard":null,"tryCatchPattern":"match atomic_write_private_file(&path, data) {\n    Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {\n        // a transaction is pending; retry after recovery clears it\n        atomic_write_private_file(&path, data)?;\n    }\n    other => other?,\n}","preventionTips":["Serialize private-file writes per directory across processes.","Retry with backoff on AlreadyExists — recovery runs at the start of each call.","Investigate crashes that leave journals behind."],"tags":["windows","concurrency","atomic-write","crash-recovery"],"backgroundTag":"file-already-exists","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}