{"record":{"id":"e94246bad09676f9","repo":"gleam-lang/gleam","slug":"non-utf8-path-in-hardlink-dir","errorCode":null,"errorMessage":"Non-UTF8 path in hardlink_dir","messagePattern":"Non-UTF8 path in hardlink_dir","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler-cli/src/fs.rs","lineNumber":766,"sourceCode":"    dest_base: &Utf8Path,\n) -> Result<(), Error> {\n    let entries = std::fs::read_dir(current).map_err(|err| Error::FileIo {\n        action: FileIoAction::Read,\n        kind: FileKind::Directory,\n        path: current.to_path_buf(),\n        err: Some(err.to_string()),\n    })?;\n\n    for entry in entries {\n        let entry = entry.map_err(|err| Error::FileIo {\n            action: FileIoAction::Read,\n            kind: FileKind::Directory,\n            path: current.to_path_buf(),\n            err: Some(err.to_string()),\n        })?;\n\n        let source_path =\n            Utf8PathBuf::from_path_buf(entry.path()).expect(\"Non-UTF8 path in hardlink_dir\");\n\n        let relative = source_path\n            .strip_prefix(base)\n            .expect(\"Source path should be under base\");\n        let dest_path = dest_base.join(relative);\n\n        let file_type = entry.file_type().map_err(|err| Error::FileIo {\n            action: FileIoAction::Read,\n            kind: FileKind::File,\n            path: source_path.clone(),\n            err: Some(err.to_string()),\n        })?;\n\n        // Skip symlinks to prevent path traversal outside the source tree\n        if file_type.is_symlink() {\n            tracing::trace!(path=?source_path, \"skipping_symlink_in_hardlink_dir\");\n            continue;\n        }","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/gleam-lang/gleam/blob/7e623aa83da3776faee50ca4ab9a6c40124acd95/compiler-cli/src/fs.rs#L748-L784","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: build cache holds a mangled name; hardlink_dir panics\ngleam build  # -> Non-UTF8 path in hardlink_dir\n\n# after: reset artefacts and fix the source name\nrm -rf build\nmv \"$(printf 'priv/data/\\xf0file.bin')\" priv/data/f0file.bin\ngleam build","handlingStrategy":"validation","validationCode":"# before cross-target builds, scan source trees AND stale build output\npython3 - <<'PY'\nimport os, sys\nbad = []\nfor root, dirs, files in os.walk(b\".\"):\n    if b\"/.git\" in root:\n        continue\n    for n in dirs + files:\n        try:\n            n.decode(\"utf-8\")\n        except UnicodeDecodeError:\n            bad.append(os.path.join(root, n))\nif bad:\n    print(*map(repr, bad), sep=\"\\n\"); sys.exit(1)\nPY\ngleam build","typeGuard":null,"tryCatchPattern":null,"preventionTips":["`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/"],"tags":["gleam","filesystem","utf-8","filename","hardlink","build-cache","camino"],"backgroundTag":"non-utf8-filename","analyzedSha":"7e623aa83da3776faee50ca4ab9a6c40124acd95","analyzedAt":"2026-08-17T00:07:02.091Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}