Hmbown/CodeWhale · 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 safety check in validate_windows_lock_handle (part of the config write-lock path). Before using the lock file handle, its metadata is inspected: if the path is not a regular file or carries the FILE_ATTRIBUTE_REPARSE_POINT flag (symlink/junction/mount point), the lock is refused to prevent lock-file redirection attacks or locking an unintended target.

Solutions

  1. Remove the symlink/junction at the lock path so a real regular file can be created there.
  2. Move the config out of a cloud-synced directory or exclude it from sync placeholders.
  3. Point the config directory at a local path instead of a symlinked/junctioned location.
  4. Verify with `dir /AL` (or `fsutil reparsepoint query`) that the lock path is no longer a reparse point, then retry.

Example fix

// before (lock path is a symlink)
C:\Users\me\.codewhale\config.lock -> D:\sync\config.lock
// after (regular file, no reparse point)
fsutil reparsepoint delete C:\Users\me\.codewhale\config.lock
del C:\Users\me\.codewhale\config.lock  # recreated as a real file on next run
Defensive patterns

Strategy: validation

Validate before calling

// PowerShell: ensure the config lock path is a regular file, not a reparse point
$lock = "$env:USERPROFILE\.codewhale\config.lock"
if (Test-Path $lock) {
  $item = Get-Item $lock -Force
  if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
    throw "$lock is a symlink/junction; remove it before running"
  }
}

Prevention

When it happens

Trigger: Acquiring the config write lock on Windows when the lock file path resolves to a reparse point — e.g. the lock file is a symlink, the config directory is a junction, or a sync tool (Dropbox/OneDrive) replaced the file with a placeholder.

Common situations: Config directory symlinked to another location; cloud-sync placeholders standing in for real files; a malicious or accidental reparse-point planted at the lock path.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/62eef92b0b9552f4. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/config_document.rs:253

    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 73e0f67d83)