gleam-lang/gleam · critical
Non-UTF8 path in hardlink_dir
Error message
Non-UTF8 path in hardlink_dir
What it means
`hardlink_dir` (compiler-cli/src/fs.rs:766) duplicates one directory tree into another via hard links while copying build artefacts (e.g. seeding a target's build cache from another compilation). It iterates `std::fs::read_dir` entries and converts each with `Utf8PathBuf::from_path_buf(entry.path()).expect("Non-UTF8 path in hardlink_dir")`. Any path in the source tree whose bytes are not valid UTF-8 panics the copy, aborting the build mid-artefact-transfer.
Source
Thrown at compiler-cli/src/fs.rs:766
dest_base: &Utf8Path,
) -> Result<(), Error> {
let entries = std::fs::read_dir(current).map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::Directory,
path: current.to_path_buf(),
err: Some(err.to_string()),
})?;
for entry in entries {
let entry = entry.map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::Directory,
path: current.to_path_buf(),
err: Some(err.to_string()),
})?;
let source_path =
Utf8PathBuf::from_path_buf(entry.path()).expect("Non-UTF8 path in hardlink_dir");
let relative = source_path
.strip_prefix(base)
.expect("Source path should be under base");
let dest_path = dest_base.join(relative);
let file_type = entry.file_type().map_err(|err| Error::FileIo {
action: FileIoAction::Read,
kind: FileKind::File,
path: source_path.clone(),
err: Some(err.to_string()),
})?;
// Skip symlinks to prevent path traversal outside the source tree
if file_type.is_symlink() {
tracing::trace!(path=?source_path, "skipping_symlink_in_hardlink_dir");
continue;
}View on GitHub (pinned to 7e623aa83d)
Solutions
- Clean the build directory first (`rm -rf build`) — copies of the offending file persist in artefact trees even after you fix the source
- Run the byte-level scanner (defense section) over BOTH the source tree and priv/ to find non-UTF-8 names
- Rename or delete each offender, then rebuild from scratch
- Fix whatever produced the invalid names (extraction charset, `convmv`, or an ASCII-only policy) so the cache is not re-polluted
Example fix
# before: build cache holds a mangled name; hardlink_dir panics gleam build # -> Non-UTF8 path in hardlink_dir # after: reset artefacts and fix the source name rm -rf build mv "$(printf 'priv/data/\xf0file.bin')" priv/data/f0file.bin gleam build
Defensive patterns
Strategy: validation
Validate before calling
# before cross-target builds, scan source trees AND stale build output
python3 - <<'PY'
import os, sys
bad = []
for root, dirs, files in os.walk(b"."):
if b"/.git" in root:
continue
for n in dirs + files:
try:
n.decode("utf-8")
except UnicodeDecodeError:
bad.append(os.path.join(root, n))
if bad:
print(*map(repr, bad), sep="\n"); sys.exit(1)
PY
gleam build Prevention
- `rm -rf build` after fixing filenames — artefact copies keep the bad bytes and re-trigger hardlink_dir
- Keep priv/ and vendored trees UTF-8-clean so the build cache never ingests invalid names
- Avoid external tools writing directly into build/
When it happens
Trigger: A non-UTF-8-named file anywhere in the tree being hardlinked — usually build output that itself copied priv/ assets or native sources with mangled names (see the walker panics), or files placed into the build directory by external tooling. The panic leaves a partially copied destination tree.
Common situations: Cross-target builds after priv/ or vendored directories with legacy-encoded filenames were picked up earlier in the pipeline; build caches polluted by other tools; artefact directories on filesystems mounted with permissive encodings.
Related errors
- Non Utf-8 Path
- stdin read_line
- `panic` expression evaluated.
- BEAM compiler instance exited: {status}
- Unable to start Tokio async runtime
AI-assisted analysis of gleam-lang/gleam@7e623aa83d (2026-08-17).
Data as JSON: /api/errors/e94246bad09676f9.
Report an issue: GitHub.