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

  1. Rename the destination to use standard Unicode (BMP) characters that round-trip on all platforms, then reissue the record.
  2. Regenerate layout records on the target platform so the encoded form matches its native encoding.
  3. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/ef1aeddbd30e657b. Report an issue: GitHub.