astrid-runtime/astrid · error · io::Error
layout destination path cannot be represented losslessly on
Error message
layout destination path cannot be represented losslessly on this platform
What it means
On non-Unix platforms, after decoding the bytes as UTF-8, encoded_bytes_to_os_string checks that OsString::from(text).as_encoded_bytes() equals the original text bytes — i.e. that the path can be represented losslessly in the platform's native path encoding (Windows uses WTF-16, which can reject certain code points). If the round-trip is not byte-identical, this InvalidData error is raised rather than silently producing a different path.
Source
Thrown at crates/astrid-core/src/dirs_layout_records.rs:249
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;
match std::fs::symlink_metadata(path) {
Err(error) if error.kind() == io::ErrorKind::NotFound => {
hasher.update(b"absent");
},
Err(error) => return Err(error),
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {
return Err(io::Error::new(View on GitHub (pinned to affd8760f4)
Solutions
- Rename the destination to use standard Unicode (BMP) characters that round-trip on all platforms, then reissue the record.
- Regenerate layout records on the target platform so the encoded form matches its native encoding.
- Drop to ASCII-only path components if cross-platform portability is required.
Example fix
// before: path with exotic code point
let dest = Path::new("/data/vol\u{D800}x"); // unpaired surrogate
// after
let dest = Path::new("/data/vol_x"); Defensive patterns
Strategy: validation
Validate before calling
let text = String::from_utf8(&encoded_bytes)?;
let os = std::ffi::OsString::from(&text);
if os.as_encoded_bytes() != text.as_bytes() {
return Err("path does not round-trip on this platform; use standard Unicode names");
} Try / catch
match physical_path(&destination) {
Err(e) if e.to_string().contains("losslessly") => rename_to_portable_path_and_reissue(),
other => other,
} Prevention
- Restrict cross-platform volume names to BMP Unicode or ASCII.
- Avoid exotic/invalid Unicode (unpaired surrogates) in paths.
- Generate layout records on the platform where they will be consumed.
When it happens
Trigger: Calling physical_path / encoded_bytes_to_os_string on Windows with UTF-8 text containing unpaired surrogates or code points that do not round-trip through the OS's OsString encoding.
Common situations: Paths written on Linux with weird/nonstandard Unicode (e.g. lone surrogate encodings via invalid WTF-8) later resolved on Windows; records produced by an older library version with laxer path encoding.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
Related errors
- layout destination path is not portable UTF-8: {error}
- legacy capsule authority entry has a non-UTF-8 name: {}
- mountpoint must be absolute
- Windows drive target is already in use: {}
- mountpoint parent is not a directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ef1aeddbd30e657b.
Report an issue: GitHub.