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

  1. Clean the build directory first (`rm -rf build`) — copies of the offending file persist in artefact trees even after you fix the source
  2. Run the byte-level scanner (defense section) over BOTH the source tree and priv/ to find non-UTF-8 names
  3. Rename or delete each offender, then rebuild from scratch
  4. 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

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


AI-assisted analysis of gleam-lang/gleam@7e623aa83d (2026-08-17). Data as JSON: /api/errors/e94246bad09676f9. Report an issue: GitHub.