rust-lang/cargo · error

path at `{}` was not valid utf-8

Error message

path at `{}` was not valid utf-8

What it means

From paths::read (crates/cargo-util/src/paths.rs:169-174). It reads file bytes via read_bytes then converts to String with String::from_utf8; on failure it bails. Cargo expects UTF-8 for every text file it parses (manifests, config, lockfile, credential tokens), so non-UTF-8 content is treated as a hard error rather than lossy.

Source

Thrown at crates/cargo-util/src/paths.rs:172

        .with_context(|| format!("failed to load metadata for path `{}`", path.display()))
}

/// Returns metadata for a file without following symlinks.
///
/// Equivalent to [`std::fs::metadata`] with better error messages.
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
    let path = path.as_ref();
    std::fs::symlink_metadata(path)
        .with_context(|| format!("failed to load metadata for path `{}`", path.display()))
}

/// Reads a file to a string.
///
/// Equivalent to [`std::fs::read_to_string`] with better error messages.
pub fn read(path: &Path) -> Result<String> {
    match String::from_utf8(read_bytes(path)?) {
        Ok(s) => Ok(s),
        Err(_) => anyhow::bail!("path at `{}` was not valid utf-8", path.display()),
    }
}

/// Reads a file into a bytes vector.
///
/// Equivalent to [`std::fs::read`] with better error messages.
pub fn read_bytes(path: &Path) -> Result<Vec<u8>> {
    fs::read(path).with_context(|| format!("failed to read `{}`", path.display()))
}

/// Writes a file to disk.
///
/// Equivalent to [`std::fs::write`] with better error messages.
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
    let path = path.as_ref();
    fs::write(path, contents.as_ref())
        .with_context(|| format!("failed to write `{}`", path.display()))
}

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Re-save the offending file as UTF-8 (most editors have an encoding command).
  2. Identify which file failed from the `{}` path in the message, then inspect its encoding (`file -i <path>`).
  3. If you genuinely need binary data, the calling code should use paths::read_bytes instead of paths::read.
  4. Restore the file from version control if it was corrupted.

Example fix

// before (caller expecting text but file is binary)
let text = cargo_util::paths::read(&path)?; // bails on non-utf8

// after (handle binary gracefully)
let bytes = cargo_util::paths::read_bytes(&path)?;
let text = match std::str::from_utf8(&bytes) {
    Ok(s) => s.to_owned(),
    Err(_) => /* lossy fallback or user error */ String::from_utf8_lossy(&bytes).into_owned(),
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate UTF-8 before assuming text
fn try_read_text(path: &std::path::Path) -> Result<String, std::io::Error> {
    let bytes = std::fs::read(path)?;
    Ok(std::str::from_utf8(&bytes)
        .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "non-utf8"))?
        .to_owned())
}

// or simply use cargo_util::paths::read_bytes when binary is acceptable

Type guard

fn is_utf8_file(path: &std::path::Path) -> bool {
    std::fs::read(path)
        .ok()
        .map(|b| std::str::from_utf8(&b).is_ok())
        .unwrap_or(false)
}

Try / catch

match cargo_util::paths::read(&path) {
    Ok(s) => s,
    Err(_) => {
        let bytes = cargo_util::paths::read_bytes(&path)?;
        String::from_utf8_lossy(&bytes).into_owned()
    }
}

Prevention

When it happens

Trigger: Cargo reads a file (Cargo.toml, .cargo/config.toml, a registry token cache file, a credential) whose bytes are not valid UTF-8. For example a manifest saved in Latin-1/GBK/Shift-JIS, or a binary file mistakenly placed where text is expected.

Common situations: An editor or legacy toolchain saved Cargo.toml in a non-UTF-8 encoding. A credential file corrupted or overwritten with binary. Git autocrlf or transcoding mishandling on Windows with legacy locales. A path collision where Cargo opens the wrong (binary) file.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/307b2c7fb5db7da2.json. Report an issue: GitHub.