{"record":{"id":"8a5d0b023c525105","repo":"xai-org/grok-build","slug":"immutable-workflow-file-already-exists","errorCode":null,"errorMessage":"immutable workflow file already exists: {}","messagePattern":"immutable workflow file already exists: (.+?)","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/session/workflow/store.rs","lineNumber":358,"sourceCode":"        ));\n    }\n    let mut bytes = Vec::with_capacity(opened.len() as usize);\n    file.take(limit.saturating_add(1)).read_to_end(&mut bytes)?;\n    if bytes.len() as u64 > limit {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"workflow artifact exceeds {limit} bytes: {}\",\n                path.display()\n            ),\n        ));\n    }\n    Ok(bytes)\n}\n\nfn atomic_write_new(path: &Path, bytes: &[u8]) -> io::Result<()> {\n    if path.exists() {\n        return Err(io::Error::new(\n            io::ErrorKind::AlreadyExists,\n            format!(\"immutable workflow file already exists: {}\", path.display()),\n        ));\n    }\n    atomic_write(path, bytes, false)\n}\n\nfn atomic_write_replace(path: &Path, bytes: &[u8]) -> io::Result<()> {\n    atomic_write(path, bytes, true)\n}\n\nfn atomic_write(path: &Path, bytes: &[u8], replace: bool) -> io::Result<()> {\n    let parent = path.parent().ok_or_else(|| {\n        io::Error::new(io::ErrorKind::InvalidInput, \"workflow path has no parent\")\n    })?;\n    std::fs::create_dir_all(parent)?;\n    let file_name = path\n        .file_name()","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/session/workflow/store.rs#L340-L376","documentation":"atomic_write_new is used for immutable files (creating a run's artifacts for the first time). If the destination already exists, it refuses with AlreadyExists and this message instead of overwriting, preserving immutability of registered workflow files.","triggerScenarios":"register() called twice with the same run id and revision; a leftover file from a previous failed run occupies the path; register racing another register of the same id.","commonSituations":"Retry logic re-invoking register after a timeout without checking id uniqueness; stale run directory from a crashed session; collision between two concurrently started runs using the same id.","solutions":["Use a fresh run id (e.g. uuid v7) for each register call","Remove the stale run directory only if it is confirmed dead/orphaned","Check for existing run id (load_workflow_runs_sync) before registering","Serialize registration through the persistence actor so duplicates cannot race"],"exampleFix":"// before\nstore.register(\"run-abc\", manifest)?; // second call -> already exists\n// after\nlet run_id = uuid::Uuid::now_v7().simple().to_string(); // unique per run\nstore.register(&run_id, manifest)?;","handlingStrategy":"try-catch","validationCode":"fn run_already_registered(runs_root: &std::path::Path, id: &str) -> bool {\n    runs_root.join(id).join(\"runs.json\").exists()\n}","typeGuard":null,"tryCatchPattern":"match store.register(&run_id, manifest) {\n    Ok(()) => {},\n    Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {\n        // id collision/stale run: pick a new id instead of overwriting\n        run_id = fresh_run_id();\n        store.register(&run_id, manifest)?;\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Generate a fresh unique run id per register call (uuid v7 simple)","Never retry register with the same id after a failure without checking existence","Clean up orphaned run dirs via an explicit GC step, not blind overwrite","Funnel registration through the persistence actor to avoid concurrent duplicate creation"],"tags":["io","already-exists","immutability","idempotency"],"backgroundTag":"file-already-exists","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}