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

legacy runtime etc contains a non-UTF-8 path

Error message

legacy runtime etc contains a non-UTF-8 path

What it means

copy_etc_state reads the legacy runtime's etc directory and converts each entry name to String so it can be checked against ETC_ALLOWLIST; a non-UTF-8 name inside etc produces this InvalidInput error. It prevents copying unknown, undecodable config paths into the new runtime layout.

Solutions

  1. Rename or remove the non-UTF-8 entry inside legacy etc/ so only allowlisted names remain
  2. Move unknown config files out of etc/ (back them up) before migrating
  3. List etc/ contents and verify each name decodes as UTF-8 and appears in the allowlist before running migration

Example fix

// before
legacy/etc/
  config.toml
  \x80backup/

// after
legacy/etc/
  config.toml
Defensive patterns

Strategy: validation

Validate before calling

fn etc_entries_clean(etc: &std::path::Path, allowlist: &[&str]) -> bool {
    std::fs::read_dir(etc)
        .map(|entries| entries.filter_map(|e| e.ok()).all(|e| {
            e.file_name().to_str().map(|n| allowlist.contains(&n)).unwrap_or(false)
        }))
        .unwrap_or(false)
}

Try / catch

match migrate_runtime(&source, ...) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("etc contains a non-UTF-8 path") =>
        eprintln!("remove or rename undecodable entries under legacy etc/ before migrating"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling migrate_runtime when a file or subdirectory inside the legacy runtime's etc/ has a non-UTF-8 name, or a name not on ETC_ALLOWLIST (which then yields the adjacent invalid-name error).

Common situations: Old config dirs containing files created with legacy encodings; manually dropped-in config files with binary names; extraneous config files added after the original install.

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/a05129253391fde7. Report an issue: GitHub.

Appendix: source

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

    }
    Ok(())
}

fn copy_etc_state(source_root: &Path, staging: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
    let source = source_root.join("etc");
    let metadata = match fs::symlink_metadata(&source) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
        Err(error) => return Err(error),
    };
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        return invalid("legacy runtime etc must be a real directory");
    }

    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 etc contains a non-UTF-8 path",
            )
        })?;
        if !ETC_ALLOWLIST.contains(&name.as_str()) {
            return invalid(&format!(
                "legacy runtime contains unsupported configuration `etc/{name}`; migration refuses to omit it"
            ));
        }
        let relative = PathBuf::from("etc").join(&name);
        copy_tree(&entry.path(), &staging.join(&relative), &relative, entries)?;
    }
    Ok(())
}

fn copy_if_present(
    source: &Path,
    destination: &Path,

View on GitHub (pinned to f6f22024fb)