sharkdp/fd · error · anyhow::Error

A path separator must be exactly one byte, but the given…

Error message

A path separator must be exactly one byte, but the given separator is {} bytes: '{}'.
In some shells on Windows, '/' is automatically expanded. Try to use '//' instead.

What it means

check_path_separator_length (main.rs:234) validates a user-supplied --path-separator on Windows: it must be exactly one byte. Multi-byte values are rejected because fd builds internal path display using byte-level joins and because some Windows shells (MSYS2/Cygwin/PowerShell) auto-expand a lone '/' into a path, prompting the hint to use '//'. Only enforced on Windows (cfg!(windows)).

Solutions

  1. Use a single-byte separator: `fd --path-separator \` or omit the flag to use the default.
  2. If your shell expands '/', quote the argument or use the native '\' separator on Windows.
  3. Avoid the flag entirely if you just want default behavior.

Example fix

# before (Windows, '//')
fd --path-separator // foo

# after (single byte)
fd --path-separator \\ foo
Defensive patterns

Strategy: validation

Validate before calling

validate_sep() {
  local s="$1"
  # Windows: must be exactly one byte
  if [ ${#s} -eq 1 ]; then
    fd --path-separator "$s" .
  else
    echo "separator must be one byte" >&2; exit 1
  fi
}

Prevention

When it happens

Trigger: On Windows, running `fd --path-separator //` or any multi-character/-byte value. The (true, Some(sep)) arm with sep.len() > 1 fires. Note this is a Windows-only guard; Unix passes the (false, _) arm.

Common situations: MSYS2/Git-Bash/PowerShell mangling a single '/' into a Windows root path, leading users to type '//' which then exceeds one byte; passing a Unicode separator character.

Related errors


AI-assisted analysis of sharkdp/fd@ee20f426dd (2026-08-09). Data as JSON: /api/errors/75aebac43011389c. Report an issue: GitHub.

Appendix: source

Thrown at src/main.rs:236

fn build_pattern_regex(pattern: &str, opts: &Opts) -> Result<String> {
    Ok(if opts.glob && !pattern.is_empty() {
        let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;
        glob.regex().to_owned()
    } else if opts.exact {
        // Anchor the escaped pattern so the full filename (or path) must match exactly.
        // Literal. No substring matching.
        format!("^{}$", regex::escape(pattern))
    } else if opts.fixed_strings {
        // Treat pattern as literal string if '--fixed-strings' is used
        regex::escape(pattern)
    } else {
        String::from(pattern)
    })
}

fn check_path_separator_length(path_separator: Option<&str>) -> Result<()> {
    match (cfg!(windows), path_separator) {
        (true, Some(sep)) if sep.len() > 1 => Err(anyhow!(
            "A path separator must be exactly one byte, but \
                 the given separator is {} bytes: '{}'.\n\
                 In some shells on Windows, '/' is automatically \
                 expanded. Try to use '//' instead.",
            sep.len(),
            sep
        )),
        _ => Ok(()),
    }
}

fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config> {
    // The search will be case-sensitive if the command line flag is set or
    // if any of the patterns has an uppercase character (smart case).
    let case_sensitive = !opts.ignore_case
        && (opts.case_sensitive
            || pattern_regexps
                .iter()

View on GitHub (pinned to ee20f426dd)