{"record":{"id":"10b3b706c29c32b8","repo":"Hmbown/CodeWhale","slug":"artifact-name-is-not-portable-utf-8","errorCode":null,"errorMessage":"artifact name is not portable UTF-8","messagePattern":"artifact name is not portable UTF-8","errorType":"validation","errorClass":"io::Error (InvalidData)","httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_export.rs","lineNumber":362,"sourceCode":"    for entry in fs::read_dir(dir)? {\n        let entry = entry?;\n        *entries += 1;\n        if *entries > MAX_ARTIFACT_ENTRIES {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"artifact tree exceeds export entry limit\",\n            ));\n        }\n        let file_type = entry.file_type()?;\n        if file_type.is_symlink() {\n            continue;\n        }\n        let child = entry.file_name();\n        let child = child\n            .to_str()\n            .filter(|name| !name.contains(['\\\\', ':']))\n            .ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"artifact name is not portable UTF-8\",\n                )\n            })?;\n        let member = format!(\"{prefix}/{child}\");\n        let path = entry.path();\n        let relative = path.strip_prefix(sessions_dir).map_err(io::Error::other)?;\n        if file_type.is_dir() {\n            // Reject substituted parent directories before descending. The file\n            // read repeats confinement checks, so directory races cannot leak bytes.\n            WorkspaceFile::open(sessions_dir, &relative.join(\".export-root-check\"), false)?;\n            collect_artifact_files_recursive(\n                sessions_dir,\n                &path,\n                &member,\n                depth + 1,\n                entries,\n                files,","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/session_export.rs#L344-L380","documentation":"Every artifact entry name must be valid UTF-8 and free of Windows-hostile characters (backslash and colon) so that names are portable in the archive and safe as archive member paths. A file whose name is non-UTF-8 (common on Linux with arbitrary bytes in filenames) or contains `\\` / `:` is rejected with InvalidData rather than producing a corrupt or non-portable archive.","triggerScenarios":"The artifacts directory contains a file created with non-UTF-8 raw bytes in its name (e.g. from a tool that wrote locale-raw names on Linux), or a name containing `\\` or `:`.","commonSituations":"Files copied from a FAT/NTFS volume or Windows machine onto a Linux artifacts dir; scripts generating names with timestamps containing `:`; archives extracted with encoding-raw filenames.","solutions":["Rename the offending files to portable ASCII names (`mv` the files detected with `convmv` or a find command).","Fix the tool writing artifacts to use UTF-8 names without `:` or `\\` (e.g. use `-` instead of `:` in timestamps).","Remove the offending files if they are disposable.","On macOS/Windows this is rare; on Linux, run `find <dir> -name *\\:* -o -name *\\\\*` and also check for invalid-UTF-8 names with `find <dir> | grep -P '[^\\x00-\\x7F]'` before exporting."],"exampleFix":"// before\n// artifacts/log:2024-01-01.txt (colon in name) -> export fails\n// after\nfs::rename(\"artifacts/log:2024-01-01.txt\", \"artifacts/log-2024-01-01.txt\")?;","handlingStrategy":"validation","validationCode":"fn portable_name(name: &std::ffi::OsStr) -> bool {\n    name.to_str().map(|s| !s.contains(['\\\\', ':'])).unwrap_or(false)\n}\n// scan artifacts: reject any entry whose file_name() fails portable_name","typeGuard":"fn as_portable_name(name: &std::ffi::OsStr) -> Option<&str> {\n    name.to_str().filter(|s| !s.contains(['\\\\', ':']))\n}","tryCatchPattern":"match write_session_archive(&session, dir, out, opts) {\n    Err(e) if e.to_string().contains(\"not portable UTF-8\") => {\n        eprintln!(\"rename non-portable artifact filenames before export\");\n    }\n    other => other?,\n}","preventionTips":["Generate artifact filenames as ASCII/UTF-8 without ':' and '\\\\' (use '-' in timestamps)","Rename files transferred from Windows/other encodings before placing them in artifacts","Scan the artifacts dir with a portable-name check before exporting on Linux"],"tags":["filesystem","encoding","utf-8","session-export","portability"],"backgroundTag":"invalid-identifier-format","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}