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
- 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`)
- Upgrade Gleam — the compiler increasingly validates/lossily-converts non-UTF-8 input before diagnostics are built
- 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
- Save all Gleam sources as UTF-8 (configure editor encoding)
- Re-encode legacy files with iconv before committing them
- When embedding the crate, use String::from_utf8_lossy over expect-based pretty_string
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Term must exist
- Unknown labels first label
- write new line after diagnostic
- at least one choice
- bit array compilation with no choice
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_modulesView on GitHub (pinned to 49f8762da5)