rust-lang/cargo · error

{}: {:?}

Error message

{}: {:?}

What it means

Rustc::cached_output runs rustc/rustdoc and converts its captured stdout to a UTF-8 String. If stdout contains invalid UTF-8 bytes, the FromUtf8Error is wrapped (with the raw bytes shown for diagnostics) and contextualized with the failing command. The result is used for rustc info caching (version, host, etc.).

Solutions

  1. Set a UTF-8 locale: `export LC_ALL=C.UTF-8 LANG=C.UTF-8` (or en_US.UTF-8).
  2. Check for a misbehaving RUSTC_WRAPPER / RUSTC_WORKSPACE_WRAPPER and remove or update it.
  3. Reinstall/verify the rustc toolchain if output is genuinely corrupted.

Example fix

# before
# LC_ALL=POSIX cargo build   # rustc prints non-utf8 banner

# after
# export LC_ALL=C.UTF-8 LANG=C.UTF-8
# cargo build
Defensive patterns

Strategy: validation

Validate before calling

fn env_uses_utf8_locale() -> bool {
    let loc = std::env::var("LC_ALL").or_else(|_| std::env::var("LANG"));
    matches!(loc, Ok(s) if s.to_ascii_uppercase().contains("UTF-8") || s.to_ascii_uppercase() == "C.UTF-8")
}

// before invoking cargo/rustc:
// assert!(env_uses_utf8_locale(), "set a UTF-8 locale to avoid rustc stdout decode errors");

Prevention

When it happens

Trigger: rustc (or a RUSTC_WRAPPER / RUSTC_WORKSPACE_WRAPPER) emits non-UTF-8 bytes on stdout during the info probe that Cargo runs at startup.

Common situations: Locale set to a non-UTF-8 encoding (C/POSIX locale); a wrapper tool (sccache, build tooling) printing binary/banner data to stdout; a corrupted rustc; malicious/broken wrapper injecting arbitrary bytes.

Related errors


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

Appendix: source

Thrown at src/util/rustc.rs:267

                    dirty: false,
                    data: CacheData::default(),
                }
            }
        }
    }

    fn cached_output(
        &mut self,
        cmd: &ProcessBuilder,
        extra_fingerprint: u64,
    ) -> CargoResult<(String, String)> {
        let key = process_fingerprint(cmd, extra_fingerprint);
        if let std::collections::hash_map::Entry::Vacant(e) = self.data.outputs.entry(key) {
            debug!("rustc info cache miss");
            debug!("running {}", cmd);
            let output = cmd.output()?;
            let stdout = String::from_utf8(output.stdout)
                .map_err(|e| anyhow::anyhow!("{}: {:?}", e, e.as_bytes()))
                .with_context(|| format!("`{}` didn't return utf8 output", cmd))?;
            let stderr = String::from_utf8(output.stderr)
                .map_err(|e| anyhow::anyhow!("{}: {:?}", e, e.as_bytes()))
                .with_context(|| format!("`{}` didn't return utf8 output", cmd))?;
            e.insert(Output {
                success: output.status.success(),
                status: if output.status.success() {
                    String::new()
                } else {
                    cargo_util::exit_status_to_string(output.status)
                },
                code: output.status.code(),
                stdout,
                stderr,
            });
            self.dirty = true;
        } else {
            debug!("rustc info cache hit");

View on GitHub (pinned to 42eee92bc9)