gleam-lang/gleam · error

Non-UTF8 path in hardlink_dir

Error message

Non-UTF8 path in hardlink_dir

What it means

hardlink_dir recursively hard-links a directory tree; for every walked entry it converts entry.path() to Utf8PathBuf with .expect("Non-UTF8 path in hardlink_dir"). Since the crate only models UTF-8 paths, a directory entry whose name contains invalid UTF-8 bytes panics with 'Non-UTF8 path in hardlink_dir'. This is an internal invariant assertion, not a recoverable error.

Solutions

  1. Scan the source directory for non-UTF-8 names (`find <source> -print0 | LC_ALL=C grep -zP '[\x80-\xFF]'`) and rename or delete them, then retry.
  2. Regenerate the source directory contents from a trusted source so all names are UTF-8.
  3. In the crate, replace the expect with a propagated Error::FileIo (or skip the entry) so a stray file yields a diagnostic rather than a panic mid-build.

Example fix

// before
let source_path =
    Utf8PathBuf::from_path_buf(entry.path()).expect("Non-UTF8 path in hardlink_dir");

// after
let source_path = Utf8PathBuf::from_path_buf(entry.path()).map_err(|p| Error::FileIo {
    action: FileIoAction::Read,
    kind: FileKind::File,
    path: p.display().to_string(),
    err: Some("non-UTF8 path in hardlink_dir".into()),
})?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: verify the hardlink source tree is UTF-8-clean before calling hardlink_dir
fn source_paths_utf8(source: &std::path::Path) -> bool {
    walkdir::WalkDir::new(source)
        .into_iter()
        .filter_map(Result::ok)
        .all(|e| e.path().to_str().is_some())
}

Type guard

fn is_utf8_path(p: &std::path::Path) -> bool {
    p.to_str().is_some()
}

Prevention

When it happens

Trigger: Calling compiler_cli::fs::hardlink_dir(source, dest) where the source tree contains any file or subdirectory with a non-UTF-8 name.

Common situations: Build cache directories or dependency checkouts containing files with byte-mangled names; artifacts unpacked from archives with non-UTF-8 entry names; files created under a non-UTF-8 locale then used in a UTF-8 build environment.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@15b07c7830 (2026-09-14). Data as JSON: /api/errors/e94246bad09676f9. Report an issue: GitHub.

Appendix: source

Thrown at compiler-cli/src/fs.rs:768

    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 15b07c7830)