astrid-runtime/astrid · error
capsule archive path is not UTF-8
Error message
capsule archive path is not UTF-8
What it means
canonical_archive_for_source walks the source archive's entries to build a canonical file list, and requires every member path to be representable as UTF-8 so it can normalize separators ('\' -> '/') and detect duplicates deterministically. An archive entry whose path bytes are not valid UTF-8 triggers this error.
Solutions
- Find the offending entry (list archive contents) and rename it to a UTF-8 name, then rebuild the archive.
- Re-create the archive with a UTF-8 locale (LC_ALL=C.UTF-8) using tar/bsdtar.
- Exclude non-UTF-8 files from the capsule source directory before packaging.
- Convert filenames with convmv (convmv -f latin1 -t utf8 -r --notest <dir>) and repackage.
Example fix
// before tar -cf capsule.tar ./src # built under a non-UTF-8 locale // after LC_ALL=C.UTF-8 tar -cf capsule.tar ./src
Defensive patterns
Strategy: validation
Validate before calling
fn all_paths_utf8(archive: &std::path::Path) -> anyhow::Result<()> {
let f = std::fs::File::open(archive)?;
let mut tar = tar::Archive::new(std::io::BufReader::new(f));
for entry in tar.entries()? {
let entry = entry?;
let p = entry.path()?.to_path_buf();
if p.to_str().is_none() {
anyhow::bail!("non-UTF-8 archive path: {}", p.display());
}
}
Ok(())
} Prevention
- Build archives under a UTF-8 locale (LC_ALL=C.UTF-8).
- Convert source-tree filenames to UTF-8 with convmv before packaging.
- Reject non-UTF-8 filenames in your packaging script.
When it happens
Trigger: archive_digest_for_source is given a capsule archive (tar) that contains an entry with non-UTF-8 filename bytes — typically archives created on filesystems with non-UTF-8 encodings or by tools writing raw byte paths.
Common situations: Packing a capsule on a legacy-locale Linux (e.g. Latin-1 filenames) or Windows tool emitting non-UTF-8 names; third-party archives downloaded from the internet containing odd entry names.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- capsule path is not valid UTF-8
- durable capsule archive path is not UTF-8
- authenticated AstridFS.app is redirected or not a directory
- authenticated macOS lifecycle tool is redirected or not…
- capsule archive contains a link or special file
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/24389b9526b5e8b9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-capsule-install/src/source_digest.rs:51
let staging = tempfile::tempdir().context("create source digest staging directory")?;
let file = fs::File::open(source)
.with_context(|| format!("open capsule archive {}", source.display()))?;
let decoder = flate2::read::GzDecoder::new(file);
let mut archive = tar::Archive::new(decoder);
let mut names = BTreeSet::new();
for entry in archive.entries().context("read capsule archive entries")? {
let mut entry = entry.context("read capsule archive entry")?;
let path = entry.path().context("read capsule archive path")?;
if path.is_absolute()
|| path
.components()
.any(|component| matches!(component, std::path::Component::ParentDir))
{
bail!("capsule archive contains an unsafe path {}", path.display());
}
let name = path
.to_str()
.ok_or_else(|| anyhow::anyhow!("capsule archive path is not UTF-8"))?
.replace('\\', "/");
if !names.insert(name.clone()) {
bail!("capsule archive contains duplicate path {name}");
}
let entry_type = entry.header().entry_type();
if !entry_type.is_dir() && !entry_type.is_file() {
bail!("capsule archive contains a link or special file {name}");
}
let destination = staging.path().join(&path);
if entry_type.is_dir() {
fs::create_dir_all(&destination)
.with_context(|| format!("create capsule archive directory {name}"))?;
continue;
}
if let Some(parent) = destination.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("create capsule archive parent for {name}"))?;
}View on GitHub (pinned to affd8760f4)