unicity-aos/aos-ce · error · io::Error

legacy runtime contains a non-UTF-8 top-level path

Error message

legacy runtime contains a non-UTF-8 top-level path

What it means

validate_source_layout iterates the top-level entries of the legacy runtime source directory and requires each name to be valid UTF-8; a non-UTF-8 top-level entry causes this InvalidInput error. The migration then classifies entries by known names (etc, bin, ...), so undecodable names are rejected upfront.

Solutions

  1. Rename or delete the non-UTF-8 top-level entry in the legacy runtime source
  2. Re-create the legacy source layout with standard UTF-8 names (etc, bin, etc.)
  3. Check entries with entry.file_name().to_str().is_some() before migrating

Example fix

// before
legacy/
  bin/
  \xff\xfe-data/   # non-UTF-8 entry

// after
legacy/
  bin/
  data/
Defensive patterns

Strategy: validation

Validate before calling

fn legacy_top_level_utf8(source: &std::path::Path) -> bool {
    std::fs::read_dir(source)
        .map(|entries| entries.filter_map(|e| e.ok()).all(|e| e.file_name().to_str().is_some()))
        .unwrap_or(false)
}

Try / catch

match migrate_runtime(&source, ...) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("non-UTF-8 top-level path") =>
        eprintln!("clean the legacy runtime root: rename or delete undecodable entries"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling migrate_runtime when the legacy runtime source directory contains a top-level file or directory with a non-UTF-8 name.

Common situations: Legacy installs created by tooling that wrote raw-byte filenames; archive extractions with broken encodings; NFS/vfat mounts lacking UTF-8 name normalization.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/13aa9837dcbf0c06. Report an issue: GitHub.

Appendix: source

Thrown at crates/unicity-aos-bootstrap/src/migration.rs:760

        let entry = entry?;
        let metadata = fs::symlink_metadata(entry.path())?;
        if metadata.file_type().is_symlink() {
            return invalid("packaged filesystem support contains a symlink");
        }
        if metadata.is_dir() {
            validate_packaged_filesystem_directory(&entry.path())?;
        } else if !metadata.is_file() {
            return invalid("packaged filesystem support contains special data");
        }
    }
    Ok(())
}

fn validate_source_layout(source: &Path) -> io::Result<()> {
    for entry in fs::read_dir(source)? {
        let entry = entry?;
        let name = entry.file_name().into_string().map_err(|_| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "legacy runtime contains a non-UTF-8 top-level path",
            )
        })?;
        let known = name == "etc"
            || name == "bin"
            || PERSISTENT_TOP_LEVEL.contains(&name.as_str())
            || EPHEMERAL_TOP_LEVEL.contains(&name.as_str());
        if !known {
            return invalid(&format!(
                "legacy runtime contains unsupported top-level state `{name}`; migration refuses to omit it"
            ));
        }
    }
    Ok(())
}

fn copy_etc_state(source_root: &Path, staging: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {

View on GitHub (pinned to f6f22024fb)