Hmbown/CodeWhale · error · anyhow::Error
config lock was redirected while opening {}
Error message
config lock was redirected while opening {} What it means
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.
Source
Thrown at crates/config/src/config_document.rs:278
let mut buffer = vec![0u16; needed as usize + 1];
// SAFETY: `buffer` is writable for its declared length and `handle` stays
// valid through the call.
let written = unsafe {
GetFinalPathNameByHandleW(handle, buffer.as_mut_ptr(), buffer.len() as u32, flags)
};
if written == 0 || written as usize >= buffer.len() {
return Err(std::io::Error::last_os_error()).with_context(|| {
format!(
"failed to resolve config lock at {}",
crate::quote_os_path(expected_path)
)
});
}
let actual = OsString::from_wide(&buffer[..written as usize]);
if normalize_windows_path_for_comparison(Path::new(&actual))?
!= normalize_windows_path_for_comparison(expected_path)?
{
bail!(
"config lock was redirected while opening {}",
crate::quote_os_path(expected_path)
);
}
Ok(())
}
#[cfg(windows)]
fn normalize_windows_path_for_comparison(path: &Path) -> Result<String> {
let text = path.to_str().ok_or_else(|| {
anyhow::anyhow!(
"config lock path {} contains invalid Unicode and cannot be compared safely",
crate::quote_os_path(path)
)
})?;
let without_device_prefix = text.strip_prefix(r"\\?\").unwrap_or(text);
let normalized_prefix = without_device_prefix.strip_prefix("UNC\\").map_or_else(
|| without_device_prefix.to_string(),View on GitHub (pinned to 0c42157ee5)
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
Example fix
# before subst X: C:\Users\me # one session uses X:\...\.config, another C:\Users\me\...\.config # after subst X: /d # use the canonical C:\Users\me\... path in all sessions
Defensive patterns
Strategy: validation
Validate before calling
// Ensure all processes use one canonical config path form:
fn canonical_config_dir() -> std::path::PathBuf {
// resolve subst/mapped drives once at startup and reuse everywhere
dunce::canonicalize(std::path::Path::new(&env::var("CODEWHALE_CONFIG").unwrap_or_default()))
.unwrap_or_else(|_| default_config_dir())
} Try / catch
match with_config_write_lock(&path, op) {
Ok(v) => v,
Err(e) if e.to_string().contains("lock was redirected") => {
// path alias mismatch: unify path form across processes and retry
user_action_then_retry(e)
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- refusing non-regular or reparse-point config lock at {}
- max_chars must be > 0
- external credential path must resolve to an absolute path: {
- config path cannot contain '..' components
- config path must not be a symlink: {}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/14650d665531e936.
Report an issue: GitHub.