Hmbown/CodeWhale · error · anyhow::Error
refusing non-regular or reparse-point config lock at {}
Error message
refusing non-regular or reparse-point config lock at {} What it means
Windows-only hardening of the config write lock: after opening the expected lock file, its metadata is inspected and the open is refused if the file is not a plain regular file or carries FILE_ATTRIBUTE_REPARSE_POINT (symlink/junction). This blocks symlink-swap and lock-redirection attacks where an attacker points the lock elsewhere so two writers can hold 'different' locks for the same config.
Source
Thrown at crates/config/src/config_document.rs:241
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt as _;
use std::os::windows::fs::MetadataExt as _;
use std::os::windows::io::AsRawHandle as _;
use windows_sys::Win32::Storage::FileSystem::{
FILE_ATTRIBUTE_REPARSE_POINT, FILE_NAME_NORMALIZED, GetFinalPathNameByHandleW,
VOLUME_NAME_DOS,
};
let metadata = file.metadata().with_context(|| {
format!(
"failed to inspect config lock at {}",
crate::quote_os_path(expected_path)
)
})?;
if !metadata.file_type().is_file()
|| metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0
{
bail!(
"refusing non-regular or reparse-point config lock at {}",
crate::quote_os_path(expected_path)
);
}
let handle = file.as_raw_handle();
let flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS;
// SAFETY: `handle` remains owned by `file`; a null output buffer asks for
// the required UTF-16 length.
let needed = unsafe { GetFinalPathNameByHandleW(handle, std::ptr::null_mut(), 0, flags) };
if needed == 0 {
return Err(std::io::Error::last_os_error()).with_context(|| {
format!(
"failed to resolve config lock at {}",
crate::quote_os_path(expected_path)
)
});
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Delete the offending lock file (it is transient and safe to remove when no Codewhale process is running) so a regular file is recreated
- Remove the symlink/junction at or above the config directory so the lock path is a direct NTFS path
- Move the config directory to a non-redirected location and point CODEWHALE/config at it
- If you intentionally redirect config via junctions, exclude the lock file name from the redirection
Example fix
# before C:\Users\me\.config\codewhale\.config.toml.lock -> symlink into dotfiles repo (reparse point) # after (PowerShell, no Codewhale running) Remove-Item -Force C:\Users\me\.config\codewhale\*.lock # next config write recreates a regular lock file
Defensive patterns
Strategy: validation
Validate before calling
// On Windows, before relying on the config lock, verify it is a regular file:
#[cfg(windows)]
fn lock_is_regular(p: &std::path::Path) -> bool {
use std::os::windows::fs::MetadataExt;
std::fs::symlink_metadata(p)
.map(|m| m.file_type().is_file() && (m.file_attributes() & 0x400) == 0) // FILE_ATTRIBUTE_REPARSE_POINT
.unwrap_or(true) // absent lock is fine; it will be created
} Try / catch
match with_config_write_lock(&path, op) {
Ok(v) => v,
Err(e) if e.to_string().contains("reparse-point config lock") => {
// instruct: delete the lock file, remove symlink indirection, retry once
user_action_then_retry(e)
}
Err(e) => return Err(e),
} Prevention
- Never symlink the config directory or lock file on Windows
- Clean stale *.lock files while Codewhale is stopped
- Keep the config path a direct NTFS path, not behind junctions/subst
When it happens
Trigger: The expected lock path on Windows resolves to a directory, a symlink/junction/hardlink-style reparse point, or another non-regular file kind when with_config_write_lock opens it.
Common situations: Config directory inside a symlinked/junctioned path (e.g. dotfiles managers, redirected profile, subst drives) that materialized the lock itself as a link; leftover malicious or tool-created symlink at the lock location; copying a config tree that preserved reparse points.
Related errors
- config lock was redirected while opening {}
- config path must not be a symlink: {}
- MCP config path must be a regular file: {}
- Codewhale-owned xAI OAuth path must not be a reparse point
- max_chars must be > 0
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/62eef92b0b9552f4.
Report an issue: GitHub.