gleam-lang/gleam · error

Error printing produced invalid utf8

Error message

Error printing produced invalid utf8

What it means

`Error::pretty_string` renders a compiler diagnostic into an in-memory `codespan` `Buffer` with colors disabled, then converts the bytes to a `String` with `String::from_utf8(...).expect("Error printing produced invalid utf8")`. The library assumes its own diagnostic rendering always emits valid UTF-8 (snippets are carved from UTF-8 source files); if the rendered bytes are not valid UTF-8, this invariant is broken and the call panics.

Solutions

  1. Ensure all project source files are saved as valid UTF-8; re-encode offending files (e.g. `iconv -f LATIN1 -t UTF-8 file.gleam > out && mv out file.gleam`)
  2. Upgrade Gleam — the compiler increasingly validates/lossily-converts non-UTF-8 input before diagnostics are built
  3. If embedding this crate, call `Error::pretty(&mut buffer)` yourself and handle the `Vec<u8>` with `String::from_utf8_lossy` instead of using `pretty_string`

Example fix

// before (library consumer)
let text = error.pretty_string();
// after
let mut buf = Buffer::no_color();
error.pretty(&mut buf);
let text = String::from_utf8_lossy(buf.into_inner()).into_owned();
Defensive patterns

Strategy: validation

Validate before calling

// ensure sources are valid UTF-8 before compiling:
let bytes = std::fs::read(path)?;
if std::str::from_utf8(&bytes).is_err() {
    eprintln!("{path} is not valid UTF-8; re-encode it before compiling");
}

Type guard

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

Try / catch

// avoid pretty_string; render to bytes and lossy-convert:
let mut buf = Buffer::no_color();
error.pretty(&mut buf);
let text = String::from_utf8_lossy(buf.into_inner());

Prevention

When it happens

Trigger: Calling `Error::pretty_string()` (directly or via the CLI's error reporting path) when the diagnostic text assembled from a source snippet or file content includes bytes that are not valid UTF-8 — e.g. reading a source file with invalid encoding on a platform that decodes paths/content lossily, or a bug where raw bytes from a non-UTF-8 file are spliced into the output buffer.

Common situations: Compiling a Gleam project on a machine where source files or paths contain non-UTF-8 bytes (legacy encodings, corrupted files) and then triggering a compiler error that quotes those bytes; historically also an issue with locale-dependent output handling.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14). Data as JSON: /api/errors/361ecfe5381815d5. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/error.rs:1001

        .map(|(option, _)| format!("Did you mean `{option}`?"))
}

fn to_ordinal(value: u32) -> String {
    match value % 10 {
        // All numbers starting with 1 end in `th` (11th, 12th, 13th, etc.)
        _ if value / 10 == 1 => format!("{value}th"),
        1 => format!("{value}st"),
        2 => format!("{value}nd"),
        3 => format!("{value}rd"),
        _ => format!("{value}th"),
    }
}

impl Error {
    pub fn pretty_string(&self) -> String {
        let mut nocolor = Buffer::no_color();
        self.pretty(&mut nocolor);
        String::from_utf8(nocolor.into_inner()).expect("Error printing produced invalid utf8")
    }

    pub fn pretty(&self, buffer: &mut Buffer) {
        for diagnostic in self.to_diagnostics() {
            diagnostic.write(buffer);
            writeln!(buffer).expect("write new line after diagnostic");
        }
    }

    pub fn skipped_files_diagnostics(&self) -> HashMap<Utf8PathBuf, Diagnostic> {
        let Error::Type {
            skipped_modules, ..
        } = self
        else {
            return HashMap::new();
        };

        skipped_modules

View on GitHub (pinned to 49f8762da5)