rust-lang/cargo · error

non UTF8 path

Error message

non UTF8 path: {}

What it means

While loading a crate's metadata from a sparse (HTTP) registry, Cargo converts the requested `path` to a `&str` via `Path::to_str()`. If the path contains non-UTF-8 bytes (OsStr not representable as UTF-8), conversion fails. Crate names in the index are always ASCII, so this indicates a malformed or non-UTF-8 path was passed to the load routine.

Solutions

  1. Ensure any path passed to registry load routines is valid UTF-8 (sanitize/normalize crate names before lookup).
  2. If calling Cargo as a library, validate `path.to_str().is_some()` before invoking load.
  3. Report a Cargo bug if this fires during normal CLI usage (crate names should be ASCII).

Example fix

// before: pass arbitrary OsStr path
let resp = backend.load(root, non_utf8_path, None).await?;

// after: validate UTF-8 first
let path_str = path.to_str()
    .ok_or_else(|| anyhow!("non UTF8 path"))?;
let resp = backend.load(root, std::path::Path::new(path_str), None).await?;
Defensive patterns

Strategy: type-guard

Validate before calling

fn require_utf8_path(path: &Path) -> Result<&str, anyhow::Error> {
    path.to_str().ok_or_else(|| anyhow::anyhow!("path is not UTF-8: {}", path.display()))
}

Type guard

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

Try / catch

let path_str = match path.to_str() {
    Some(s) => s,
    None => { tracing::warn!("skipping non-UTF-8 path"); return Ok(LoadResponse::NotFound); }
};

Prevention

When it happens

Trigger: `load()` is called with a `&Path` whose OsStr is not valid UTF-8; `.to_str()` returns `None`. Practically unreachable from normal Cargo flows (crate names are validated ASCII) but could be hit by programmatic callers passing arbitrary paths or on platforms with non-UTF-8 path encoding.

Common situations: Programmatic use of Cargo as a library passing non-UTF-8 paths; filesystem locale producing non-UTF-8 path bytes; corrupted in-memory path construction.

Related errors


AI-assisted analysis of rust-lang/cargo@eb98b54bc9 (2026-08-11). Data as JSON: /api/errors/ac6c35ce6e271471. Report an issue: GitHub.

Appendix: source

Thrown at src/sources/registry/http_remote.rs:283

    }

    async fn load(
        &self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> CargoResult<LoadResponse> {
        // Ensure the config is loaded.
        let Some(config) = self.config_opt().await? else {
            return Ok(LoadResponse::NotFound);
        };
        self.inner()
            .auth_required
            .update(|v| v || config.auth_required);

        let path = path
            .to_str()
            .ok_or_else(|| anyhow::anyhow!("non UTF8 path: {}", path.display()))?;
        self.sparse_fetch(path, index_version).await
    }

    async fn config(&self) -> CargoResult<Option<RegistryConfig>> {
        Ok(Some(self.config().await?))
    }

    fn invalidate_cache(&self) {
        // Actually updating the index is more or less a no-op for this implementation.
        // All it does is ensure that a subsequent load will double-check files with the
        // server rather than rely on a locally cached copy of the index files.
        debug!("invalidated index cache");
        self.inner().fresh.borrow_mut().clear();
        self.inner().requested_update.set(true);
    }

    fn set_quiet(&mut self, quiet: bool) {
        self.inner().quiet.set(quiet);

View on GitHub (pinned to eb98b54bc9)