{"record":{"id":"25d3aece1d73e271","repo":"Hmbown/CodeWhale","slug":"alreadyexists","errorCode":"AlreadyExists","errorMessage":"immutable artifact handle already contains different bytes","messagePattern":"immutable artifact handle already contains different bytes","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/artifacts.rs","lineNumber":181,"sourceCode":"/// A duplicate replay with identical bytes is idempotent; a different payload\n/// for the same relative path fails closed.\npub fn write_session_relative_immutable(\n    session_id: &str,\n    relative_path: &Path,\n    content: &[u8],\n) -> io::Result<PathBuf> {\n    let absolute_path =\n        session_artifact_absolute_path(session_id, relative_path).ok_or_else(|| {\n            io::Error::new(io::ErrorKind::InvalidInput, \"invalid session artifact path\")\n        })?;\n    if let Some(parent) = absolute_path.parent() {\n        std::fs::create_dir_all(parent)?;\n    }\n    if absolute_path.exists() {\n        return if std::fs::read(&absolute_path)? == content {\n            Ok(absolute_path)\n        } else {\n            Err(io::Error::new(\n                io::ErrorKind::AlreadyExists,\n                \"immutable artifact handle already contains different bytes\",\n            ))\n        };\n    }\n    let file_name = absolute_path\n        .file_name()\n        .and_then(|name| name.to_str())\n        .unwrap_or(\"artifact\");\n    let temp_path = absolute_path.with_file_name(format!(\n        \".{file_name}.{}.{}.tmp\",\n        std::process::id(),\n        uuid::Uuid::new_v4()\n    ));\n    let publish = (|| -> io::Result<()> {\n        let mut file = std::fs::OpenOptions::new()\n            .write(true)\n            .create_new(true)","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/artifacts.rs#L163-L199","documentation":"write_session_relative_immutable publishes write-once artifacts: if the target already exists and its bytes differ from `content`, it fails closed with AlreadyExists; identical bytes are an idempotent Ok. This guarantees a replayed or concurrent session cannot silently change what an earlier artifact handle points to — the artifact contract is one immutable payload per (session, relative path).","triggerScenarios":"Two different payloads written under the same session_id + relative_path: a tool regenerating output under a stable artifact name, a resumed session producing divergent content, or two distinct raw ids colliding after sanitize_id_component maps them to the same sanitized name (e.g. 'a/b' and 'a_b').","commonSituations":"Nondeterministic tool output (timestamps, random ids) reused under a fixed artifact name; replaying an edited session; duplicate tool-call ids from a buggy provider stream.","solutions":["Derive artifact ids from content identity (hash) or the unique tool_call_id so different bytes always take different paths","If the existing artifact is stale, delete that session's artifacts directory (or the single file) before re-recording","Make the payload deterministic for replays (strip volatile fields like timestamps before publishing)","Treat the error as a caller-side contract violation: audit who wrote the first payload and why it differs"],"exampleFix":"// before\nlet rel = session_artifact_relative_path(&format!(\"art_{name}\")); // fixed name, changing bytes\nlet abs = write_session_relative_immutable(sid, &rel, bytes)?; // AlreadyExists\n\n// after: content-addressed id, collisions are now identical bytes\nlet digest = sha256(&bytes);\nlet rel = session_artifact_relative_path(&format!(\"art_{name}_{digest}\"));\nlet abs = write_session_relative_immutable(sid, &rel, bytes)?;","handlingStrategy":"fallback","validationCode":"let target = session_artifact_absolute_path(sid, &rel);\nif let Some(abs) = &target {\n    if let Ok(existing) = std::fs::read(abs) {\n        if existing != bytes {\n            // choose a new unique relative path now, before the failing write\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"match write_session_relative_immutable(sid, &rel, bytes) {\n    Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {\n        // same path, different bytes: republish under a fresh content-derived id\n        // do NOT delete+overwrite in place unless you own the whole session\n    }\n    other => other?,\n}","preventionTips":["Make artifact ids content-derived (hash) or tool-call-unique so different bytes never share a path","Keep payloads deterministic across replays","Treat AlreadyExists-with-different-bytes as a caller contract violation to log, not to paper over"],"tags":["artifacts","immutability","idempotency","write-once"],"backgroundTag":"file-already-exists","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}