{"record":{"id":"a61998c0dbb770b3","repo":"astrid-runtime/astrid","slug":"invalidinput","errorCode":"InvalidInput","errorMessage":"staged executable is not on the live executable volume","messagePattern":"staged executable is not on the live executable volume","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs/windows/executable.rs","lineNumber":223,"sourceCode":"    for name in names {\n        let source = extract_dir.join(name);\n        extract_guard.verify()?;\n        install_guard.verify()?;\n        let staged_name = format!(\".{name}.{transaction_id}.new\");\n        let (temporary, new_hash) = stage_transaction_copy_authenticated(\n            install_guard,\n            extract_guard,\n            FileContract::Trusted,\n            BoundaryContract::TrustedForCreate,\n            install_dir,\n            &source,\n            &staged_name,\n        )?;\n        cleanup.track(temporary.clone());\n        #[cfg(test)]\n        test_maybe_fail_preparation(journal.entries.len())?;\n        if volume_root(&temporary)? != install_volume {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"staged executable is not on the live executable volume\",\n            ));\n        }\n        let live = install_dir.join(name);\n        let had_live = guarded_file_exists(install_guard, &live)?;\n        let (rollback, old_hash) = if had_live {\n            let rollback_name = format!(\".{name}.{transaction_id}.rollback\");\n            let (rollback_path, old_hash) = stage_transaction_copy_authenticated(\n                install_guard,\n                install_guard,\n                FileContract::Trusted,\n                BoundaryContract::TrustedForCreate,\n                install_dir,\n                &live,\n                &rollback_name,\n            )?;\n            cleanup.track(rollback_path);","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs/windows/executable.rs#L205-L241","documentation":"An executable replacement requires the staged (temporary) file to live on the same NTFS volume as the live install directory so the final swap can be atomic. prepare_executable_transaction compares volume_root(staged) with the install volume and rejects with InvalidInput when they differ. No files are modified when this fires.","triggerScenarios":"Calling replace_executable_set with a staged/temporary path or install directory configuration where the staging location resolves to a different volume than the live executable directory (e.g. staging on a temp drive, install on C:).","commonSituations":"TEMP on a different drive from Program Files; mapped or junctioned install paths whose volume_root differs; container or CI setups mounting install and temp on separate volumes; users relocating the install directory without updating staging config.","solutions":["Configure the staging/temp location to be on the same volume as the install directory","Verify the install directory is not behind a junction/subst/mapped path that changes its volume root","Canónico the paths (canonicalize) before the call so volume_root computes the intended volume","If cross-volume installs are unavoidable, copy staged bytes into a temporary file on the install volume first"],"exampleFix":"// before: staging in %TEMP% on D: while installing to C:\\Program Files\\App\nlet staged = temp_dir().join(\"staged.exe\");\n// after: stage inside the install volume\nlet staged = install_dir.join(\".staging\").join(\"staged.exe\");","handlingStrategy":"validation","validationCode":"// Confirm staging and install paths share a volume before the call\nfn same_volume(a: &std::path::Path, b: &std::path::Path) -> bool {\n    use std::os::windows::ffi::OsStrExt;\n    // compare volume root components of canonicalized a and b\n    a.canonicalize().ok().zip(b.canonicalize().ok())\n        .map(|(a, b)| {\n            let av: String = a.components().take(1).map(|c| c.as_os_str().to_string_lossy().into_owned()).collect();\n            let bv: String = b.components().take(1).map(|c| c.as_os_str().to_string_lossy().into_owned()).collect();\n            av.eq_ignore_ascii_case(&bv)\n        }).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"if !same_volume(&staged, &install_dir) {\n    // stage on the install volume first, then call replace_executable_set\n}","preventionTips":["Keep staging/temp on the same drive as the install directory","Canonicalize junctions/mapped paths before computing volume roots","Mount install and temp directories on the same volume in CI/containers"],"tags":["windows","fs","path","update-transaction"],"backgroundTag":"invalid-argument-value","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"}