Hmbown/CodeWhale · error

config lock path contains invalid Unicode and cannot be…

Error message

config lock path {} contains invalid Unicode and cannot be compared safely

What it means

On Windows, `normalize_windows_path_for_comparison` converts lock-file paths to a canonical string form (stripping `\\?\` device and `UNC\` prefixes) so lock handles can be compared. If the path cannot be represented as valid Unicode (`Path::to_str()` returns None), comparison would be unsafe, so it fails with this error. Only reachable on Windows.

Solutions

  1. Rename the config directory (or set the config path) to a location using only valid Unicode characters.
  2. Check the config-path environment/CLI override for accidentally garbled bytes.
  3. Ensure the path is valid UTF-8: use `PathBuf` from UTF-8 strings, not raw byte sources.
Defensive patterns

Strategy: validation

Validate before calling

// Windows-only guard before configuring a custom lock path
if path.as_os_str().to_str().is_none() {
    return Err("config lock path must be valid Unicode (UTF-8)");
}

Type guard

fn is_valid_unicode_path(p: &std::path::Path) -> bool {
    p.to_str().is_some()
}

Prevention

When it happens

Trigger: Calling code that validates a Windows lock handle (`validate_windows_lock_handle`) with a path built from invalid UTF-8 bytes (e.g. from `OsString`/raw filesystem APIs) in the config lock path.

Common situations: Config directory placed under a path containing non-Unicode bytes, or paths assembled from legacy Windows APIs using the system ANSI codepage.

Related errors


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

Appendix: source

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

            )
        });
    }
    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(),
        |rest| format!(r"\\{rest}"),
    );
    Ok(normalized_prefix
        .replace('/', "\\")
        .trim_end_matches('\\')
        .to_lowercase())
}

fn prepare_config_path(path: &Path) -> Result<PathBuf> {
    let absolute = if path.is_absolute() {
        path.to_path_buf()

View on GitHub (pinned to 73e0f67d83)