{"record":{"id":"eb92a7f59c7e2876","repo":"astrid-runtime/astrid","slug":"capability-relative-exclusive-rename-is-unsupporte","errorCode":null,"errorMessage":"capability-relative exclusive rename is unsupported","messagePattern":"capability-relative exclusive rename is unsupported","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-storage/src/principal_state/native_io.rs","lineNumber":631,"sourceCode":"\n#[cfg(windows)]\nfn rename_no_replace(\n    source_directory: &Dir,\n    source: &Path,\n    destination_directory: &Dir,\n    destination: &Path,\n) -> std::io::Result<()> {\n    source_directory.rename(source, destination_directory, destination)\n}\n\n#[cfg(not(any(target_os = \"linux\", target_os = \"macos\", windows)))]\nfn rename_no_replace(\n    _source_directory: &Dir,\n    _source: &Path,\n    _destination_directory: &Dir,\n    _destination: &Path,\n) -> std::io::Result<()> {\n    Err(std::io::Error::new(\n        std::io::ErrorKind::Unsupported,\n        \"capability-relative exclusive rename is unsupported\",\n    ))\n}\n\npub(super) fn private_file_identity(file: &File) -> StorageResult<PrivateFileIdentity> {\n    let metadata = file\n        .metadata()\n        .map_err(|error| connection(format!(\"inspect private file handle: {error}\")))?;\n    if !metadata.is_file() {\n        return Err(connection(\n            \"private file handle is not a regular file\".to_owned(),\n        ));\n    }\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::MetadataExt as _;\n        use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage/src/principal_state/native_io.rs#L613-L649","documentation":"rename_no_replace performs an exclusive (no-replace) rename relative to two capability directories. On platforms/APIs lacking a no-replace rename (e.g. no renameat2 with RENAME_NOREPLACE, no MoveFileEx fallback), this stub returns ErrorKind::Unsupported. The operation is deliberately not emulated with a non-atomic check-then-rename, because that would lose the exclusivity guarantee.","triggerScenarios":"Calling rename_no_replace on a platform where the cfg-gated native implementation is absent — the fallback body unconditionally errors. Any principal-state flow performing exclusive renames on such a target hits this.","commonSituations":"Porting the storage layer to an OS without renameat2/RENAME_NOREPLACE semantics; older kernels lacking renameat2 on Linux; exotic filesystems where the syscall returns ENOSYS at build-time feature detection.","solutions":["Run on a platform whose libc provides atomic no-replace rename (Linux with renameat2, Windows MoveFileEx, macOS renamex_np)","Upgrade the kernel/OS so the atomic-rename syscall is available","File or contribute a platform implementation for rename_no_replace in principal_state/native_io.rs","Restructure the caller to use a lock file or create-new (O_EXCL) protocol instead of exclusive rename if the platform truly cannot support it"],"exampleFix":"// before\nrename_no_replace(&src_dir, &src, &dst_dir, &dst)?; // Unsupported on this platform\n// after\nlet opts = OpenOptions::new().write(true).create_new(true);\nlet mut lock = opts.open(&dst)?; // O_EXCL claim, then write contents\nlock.write_all(&payload)?;","handlingStrategy":"fallback","validationCode":"if !atomic_no_replace_rename_supported() {\n    // use lock-file or O_EXCL create protocol instead\n    use_create_new_protocol();\n}","typeGuard":"const EXCLUSIVE_RENAME_SUPPORTED: bool = cfg!(target_os = \"linux\") || cfg!(target_os = \"windows\");","tryCatchPattern":"match rename_result {\n    Err(e) if e.kind() == io::ErrorKind::Unsupported => {\n        // fall back to O_EXCL create-new claiming protocol\n    }\n    r => r,\n}","preventionTips":["Confirm the target kernel/OS supports atomic no-replace rename before relying on exclusive renames","Design callers with a fallback claiming protocol (create_new + write)","Detect ENOSYS/Unsupported at startup rather than mid-transaction","Keep the storage layer on platforms with full native_io support"],"tags":["platform","unsupported","filesystem","rename"],"backgroundTag":"unsupported-operation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}