{"record":{"id":"14650d665531e936","repo":"Hmbown/CodeWhale","slug":"config-lock-was-redirected-while-opening","errorCode":null,"errorMessage":"config lock was redirected while opening {}","messagePattern":"config lock was redirected while opening (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/config_document.rs","lineNumber":278,"sourceCode":"    let mut buffer = vec![0u16; needed as usize + 1];\n    // SAFETY: `buffer` is writable for its declared length and `handle` stays\n    // valid through the call.\n    let written = unsafe {\n        GetFinalPathNameByHandleW(handle, buffer.as_mut_ptr(), buffer.len() as u32, flags)\n    };\n    if written == 0 || written as usize >= buffer.len() {\n        return Err(std::io::Error::last_os_error()).with_context(|| {\n            format!(\n                \"failed to resolve config lock at {}\",\n                crate::quote_os_path(expected_path)\n            )\n        });\n    }\n    let actual = OsString::from_wide(&buffer[..written as usize]);\n    if normalize_windows_path_for_comparison(Path::new(&actual))?\n        != normalize_windows_path_for_comparison(expected_path)?\n    {\n        bail!(\n            \"config lock was redirected while opening {}\",\n            crate::quote_os_path(expected_path)\n        );\n    }\n    Ok(())\n}\n\n#[cfg(windows)]\nfn normalize_windows_path_for_comparison(path: &Path) -> Result<String> {\n    let text = path.to_str().ok_or_else(|| {\n        anyhow::anyhow!(\n            \"config lock path {} contains invalid Unicode and cannot be compared safely\",\n            crate::quote_os_path(path)\n        )\n    })?;\n    let without_device_prefix = text.strip_prefix(r\"\\\\?\\\").unwrap_or(text);\n    let normalized_prefix = without_device_prefix.strip_prefix(\"UNC\\\\\").map_or_else(\n        || without_device_prefix.to_string(),","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/config/src/config_document.rs#L260-L296","documentation":"Windows-only companion to the reparse-point check: the opened lock file handle is resolved to its final NTFS path via GetFinalPathNameByHandleW and compared (normalized) against the expected path. A mismatch means the open was redirected — the file you locked is not the file at the expected path, so locking would not actually serialize writers.","triggerScenarios":"Opening the expected lock path actually yielded a different final path: 8.3 short-name mismatch handled differently, subst/ mapped-drive prefixes, a junction inside the path chain, or case/normalization differences that survive the comparison function.","commonSituations":"Config accessed through a subst drive or mapped network path in one process and the real path in another; drive-letter vs `\\\\?\\` volume-name prefixes; junction-based profile redirection changing the final path.","solutions":["Always invoke Codewhale with the same form of config path (same drive letter, no mixed subst/mapped aliases) across processes","Remove subst/mapped-drive or junction indirection from the config directory path","Delete stale lock files and let the next write recreate them under the consistent path"],"exampleFix":"# before\nsubst X: C:\\Users\\me   # one session uses X:\\...\\.config, another C:\\Users\\me\\...\\.config\n\n# after\nsubst X: /d\n# use the canonical C:\\Users\\me\\... path in all sessions","handlingStrategy":"validation","validationCode":"// Ensure all processes use one canonical config path form:\nfn canonical_config_dir() -> std::path::PathBuf {\n    // resolve subst/mapped drives once at startup and reuse everywhere\n    dunce::canonicalize(std::path::Path::new(&env::var(\"CODEWHALE_CONFIG\").unwrap_or_default()))\n        .unwrap_or_else(|_| default_config_dir())\n}","typeGuard":null,"tryCatchPattern":"match with_config_write_lock(&path, op) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"lock was redirected\") => {\n        // path alias mismatch: unify path form across processes and retry\n        user_action_then_retry(e)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Invoke Codewhale from a consistent path form (avoid mixing subst/mapped drives with real paths)","Set CODEWHALE_CONFIG once in your environment so every session resolves the same directory","Remove junction indirection above the config directory"],"tags":["windows","lock","path-resolution","security","config"],"backgroundTag":"symlink-security-check","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}