astrid-runtime/astrid · error · io::Error
layout destination path is not portable UTF-8: {error}
Error message
layout destination path is not portable UTF-8: {error} What it means
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.
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:242
}
#[cfg(unix)]
#[expect(
clippy::unnecessary_wraps,
reason = "the cross-platform receipt decoder has one fallible signature"
)]
pub(super) fn encoded_bytes_to_os_string(bytes: Vec<u8>) -> io::Result<OsString> {
use std::os::unix::ffi::OsStringExt as _;
// Unix receipts commit to the raw OsStr byte sequence, including paths
// that are not UTF-8. Decoding must therefore preserve every byte.
Ok(OsString::from_vec(bytes))
}
#[cfg(not(unix))]
pub(super) fn encoded_bytes_to_os_string(bytes: Vec<u8>) -> io::Result<OsString> {
let text = String::from_utf8(bytes).map_err(|error| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("layout destination path is not portable UTF-8: {error}"),
)
})?;
let path = OsString::from(&text);
if path.as_encoded_bytes() != text.as_bytes() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"layout destination path cannot be represented losslessly on this platform",
));
}
Ok(path)
}
pub(super) fn inventory_tree(path: &Path) -> io::Result<LayoutTreeIdentityV1> {
let mut hasher = blake3::Hasher::new_derive_key("astrid layout source inventory v1");
let mut entries = 0_u64;
let mut bytes = 0_u64;View on GitHub (pinned to affd8760f4)
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.
Example fix
// before: non-UTF-8 filename on Linux
mv $'caf\xe9' café
// after: rewrite receipt against the clean path
let dest = Path::new("/data/café/volume.bin"); // valid UTF-8 Defensive patterns
Strategy: validation
Validate before calling
if std::str::from_utf8(&encoded_bytes).is_err() {
return Err("recorded path bytes are not valid UTF-8; regenerate on a UTF-8 filesystem");
} Try / catch
match physical_path(&destination) {
Err(e) if e.to_string().contains("not portable UTF-8") => regenerate_records_with_utf8_paths(),
other => other,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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
- legacy capsule authority entry has a non-UTF-8 name: {}
- capsule path is not valid UTF-8
- layout destination path cannot be represented losslessly on
- legacy principal-home source {path}: principal directory nam
- legacy capsule entry has a non-UTF-8 name
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/cacc5929497ebba1.
Report an issue: GitHub.