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

imported capsule installation contains a non-UTF-8 name

Error message

imported capsule installation contains a non-UTF-8 name

What it means

Inside `archive_inactive_activation_state`, the inactive-capsules scan converts each capsule directory entry name with `into_string()`. If a capsule installation name under the imported state is not valid UTF-8, it cannot be represented in the migration manifest, so an `InvalidInput` error is raised.

Solutions

  1. List the inactive capsule directory byte-wise (`LC_ALL=C ls`) to identify the non-UTF-8 entry and rename it with a valid UTF-8 name.
  2. Reinstall or re-extract the affected capsule so its directory name is UTF-8, then retry the migration.
  3. Remove the offending capsule entry if it is no longer needed (it is inactive state and cannot be archived otherwise).
  4. Normalize the source state on a UTF-8 locale system (e.g. `convmv --notest -f latin1 -t utf8 -r <capsules-dir>`) before migrating.

Example fix

// shell: fix non-UTF-8 capsule directory name before migration
// before
convmv -f latin1 -t utf8 -r --notest run/capsules/

// after: all capsule names are valid UTF-8, migration proceeds
Defensive patterns

Strategy: validation

Validate before calling

for entry in fs::read_dir(capsules_dir)? {
    let entry = entry?;
    if entry.file_name().to_str().is_none() {
        return Err(anyhow!("non-UTF-8 capsule name: {:?}", entry.file_name()));
    }
}

Type guard

fn is_utf8_name(e: &std::fs::DirEntry) -> bool { e.file_name().to_str().is_some() }

Try / catch

if err.to_string().contains("non-UTF-8 name") {
    eprintln!("rename or remove the offending capsule directory and retry");
}

Prevention

When it happens

Trigger: Running `migrate_runtime` where the imported inactive capsule installation directory contains an entry whose filename has non-UTF-8 bytes (e.g. capsule dirs created by scripts using legacy encodings).

Common situations: Capsule directories extracted from zip archives with non-UTF-8 filename flags; directories produced on a system with a non-UTF-8 locale; manual file operations using raw byte names.

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

Appendix: source

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

        let capsules_metadata = match fs::symlink_metadata(&capsules) {
            Ok(metadata) => metadata,
            Err(error) if error.kind() == io::ErrorKind::NotFound => continue,
            Err(error) => return Err(error),
        };
        if capsules_metadata.file_type().is_symlink() || !capsules_metadata.is_dir() {
            return invalid("imported capsule installation root must be a real directory");
        }

        if principal_name != "default" {
            archive_path(staging, &relative, entries)?;
            continue;
        }

        let mut inactive_capsules = Vec::new();
        for capsule in fs::read_dir(&capsules)? {
            let capsule = capsule?;
            let name = capsule.file_name().into_string().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "imported capsule installation contains a non-UTF-8 name",
                )
            })?;
            let metadata = fs::symlink_metadata(capsule.path())?;
            if metadata.file_type().is_symlink() {
                return invalid("imported capsule installation must not be a symlink");
            }
            if !metadata.is_dir() && !metadata.is_file() {
                return invalid("imported capsule installation contains a special file");
            }
            let active_ce_capsule = metadata.is_dir()
                && imported_capsule_package_name(&capsule.path()).as_deref() == Some(&name)
                && ce_capsules.contains(&name);
            if !active_ce_capsule {
                inactive_capsules.push(name);
            }
        }

View on GitHub (pinned to f6f22024fb)