vectordotdev/vector · error

Failed to convert OsStr to &str for

Error message

Failed to convert OsStr to &str for: {input_path:?}

What it means

After obtaining the extension as an OsStr, convert_config converts it to &str; this fails only when the extension is not valid UTF-8. In that case it panics with 'Failed to convert OsStr to &str for: <path>'.

Solutions

  1. Rename the file to use a plain ASCII/UTF-8 extension (e.g. mv 'file.$\xff' file.yaml)
  2. Check the filename bytes with `ls | cat -v` or `printf '%s' | iconv` and fix the encoding
  3. Patch the tool to use to_string_lossy() and skip or error on non-UTF-8 extensions gracefully

Example fix

// before
.unwrap_or_else(|| panic!("Failed to convert OsStr to &str for: {input_path:?}"))
// after
.unwrap_or_else(|| {
    eprintln!("Skipping non-UTF-8 path: {input_path:?}");
    ""
})
Defensive patterns

Strategy: validation

Validate before calling

fn is_utf8_ext(p: &std::path::Path) -> bool {
    p.extension().map(|e| e.to_str().is_some()).unwrap_or(false)
}

Type guard

fn utf8_ext(p: &std::path::Path) -> Option<&str> { p.extension()?.to_str() }

Prevention

When it happens

Trigger: Input file paths containing non-UTF-8 bytes in the extension, typically from filenames created with a non-UTF-8 locale or raw bytes (common on some Linux systems).

Common situations: Files downloaded or extracted from archives with non-UTF-8 names, Samba/NFS-mounted shares with legacy encodings, or filenames with corrupt byte sequences in the extension.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/eca81ec9d97dcde7. Report an issue: GitHub.

Appendix: source

Thrown at src/convert_config.rs:119

            }
        }
    }
}

fn convert_config(
    input_path: &Path,
    output_path: &Path,
    output_format: Format,
) -> Result<(), Vec<String>> {
    if output_path.exists() {
        return Err(vec![format!("Output path {output_path:?} exists")]);
    }
    let input_format = match Format::from_str(
        input_path
            .extension()
            .unwrap_or_else(|| panic!("Failed to get extension for: {input_path:?}"))
            .to_str()
            .unwrap_or_else(|| panic!("Failed to convert OsStr to &str for: {input_path:?}")),
    ) {
        Ok(format) => format,
        Err(_) => return Ok(()), // skip irrelevant files
    };

    if input_format == output_format {
        return Ok(());
    }

    #[allow(clippy::print_stdout)]
    {
        println!("Converting {input_path:?} config to {output_format:?}.");
    }
    let file_contents = fs::read_to_string(input_path).map_err(|e| vec![e.to_string()])?;
    let builder: ConfigBuilder = format::deserialize(&file_contents, input_format)?;
    let config = builder.build()?;
    let output_string = format::serialize(&config, output_format).map_err(|e| vec![e])?;
    fs::write(output_path, output_string).map_err(|e| vec![e.to_string()])?;

View on GitHub (pinned to bdb87aeaa4)