{"record":{"id":"cacc5929497ebba1","repo":"astrid-runtime/astrid","slug":"layout-destination-path-is-not-portable-utf-8-er","errorCode":null,"errorMessage":"layout destination path is not portable UTF-8: {error}","messagePattern":"layout destination path is not portable UTF-8: (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout_records.rs","lineNumber":242,"sourceCode":"}\n\n#[cfg(unix)]\n#[expect(\n    clippy::unnecessary_wraps,\n    reason = \"the cross-platform receipt decoder has one fallible signature\"\n)]\npub(super) fn encoded_bytes_to_os_string(bytes: Vec<u8>) -> io::Result<OsString> {\n    use std::os::unix::ffi::OsStringExt as _;\n\n    // Unix receipts commit to the raw OsStr byte sequence, including paths\n    // that are not UTF-8. Decoding must therefore preserve every byte.\n    Ok(OsString::from_vec(bytes))\n}\n\n#[cfg(not(unix))]\npub(super) fn encoded_bytes_to_os_string(bytes: Vec<u8>) -> io::Result<OsString> {\n    let text = String::from_utf8(bytes).map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"layout destination path is not portable UTF-8: {error}\"),\n        )\n    })?;\n    let path = OsString::from(&text);\n    if path.as_encoded_bytes() != text.as_bytes() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"layout destination path cannot be represented losslessly on this platform\",\n        ));\n    }\n    Ok(path)\n}\n\npub(super) fn inventory_tree(path: &Path) -> io::Result<LayoutTreeIdentityV1> {\n    let mut hasher = blake3::Hasher::new_derive_key(\"astrid layout source inventory v1\");\n    let mut entries = 0_u64;\n    let mut bytes = 0_u64;","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout_records.rs#L224-L260","documentation":"On non-Unix platforms, encoded_bytes_to_os_string converts receipt-encoded path bytes back into an OsString by requiring the bytes to be valid UTF-8. If the stored path bytes are not valid UTF-8 (e.g. written on a Unix system with raw non-UTF-8 bytes), the conversion fails with this InvalidData error including the underlying Utf8Error. This enforces portability of layout destination paths across platforms.","triggerScenarios":"Calling physical_path (or any receipt decoding that reaches encoded_bytes_to_os_string) on a non-Unix platform where the encoded destination path bytes contain invalid UTF-8 sequences.","commonSituations":"Receipts or records produced on Linux with non-UTF-8 file names (e.g. Latin-1 encoded bytes) then consumed on Windows/macOS; corrupted receipt bytes; hand-edited binary records.","solutions":["Regenerate the layout records on a system where the path is valid UTF-8.","Rename the destination path to use only UTF-8/ASCII characters and rewrite the receipt.","Inspect the encoded bytes with a hex dump to find and repair the invalid sequence in the source record."],"exampleFix":"// before: non-UTF-8 filename on Linux\nmv $'caf\\xe9' café\n// after: rewrite receipt against the clean path\nlet dest = Path::new(\"/data/café/volume.bin\"); // valid UTF-8","handlingStrategy":"validation","validationCode":"if std::str::from_utf8(&encoded_bytes).is_err() {\n    return Err(\"recorded path bytes are not valid UTF-8; regenerate on a UTF-8 filesystem\");\n}","typeGuard":null,"tryCatchPattern":"match physical_path(&destination) {\n    Err(e) if e.to_string().contains(\"not portable UTF-8\") => regenerate_records_with_utf8_paths(),\n    other => other,\n}","preventionTips":["Use only UTF-8/ASCII file names for volumes shared across platforms.","Fix non-UTF-8 names on Linux before producing layout records.","Treat generated records as binary; never hand-edit their byte payloads."],"tags":["filesystem","encoding","utf-8","portability"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}