{"record":{"id":"24389b9526b5e8b9","repo":"astrid-runtime/astrid","slug":"capsule-archive-path-is-not-utf-8","errorCode":null,"errorMessage":"capsule archive path is not UTF-8","messagePattern":"capsule archive path is not UTF-8","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/source_digest.rs","lineNumber":51,"sourceCode":"    let staging = tempfile::tempdir().context(\"create source digest staging directory\")?;\n    let file = fs::File::open(source)\n        .with_context(|| format!(\"open capsule archive {}\", source.display()))?;\n    let decoder = flate2::read::GzDecoder::new(file);\n    let mut archive = tar::Archive::new(decoder);\n    let mut names = BTreeSet::new();\n    for entry in archive.entries().context(\"read capsule archive entries\")? {\n        let mut entry = entry.context(\"read capsule archive entry\")?;\n        let path = entry.path().context(\"read capsule archive path\")?;\n        if path.is_absolute()\n            || path\n                .components()\n                .any(|component| matches!(component, std::path::Component::ParentDir))\n        {\n            bail!(\"capsule archive contains an unsafe path {}\", path.display());\n        }\n        let name = path\n            .to_str()\n            .ok_or_else(|| anyhow::anyhow!(\"capsule archive path is not UTF-8\"))?\n            .replace('\\\\', \"/\");\n        if !names.insert(name.clone()) {\n            bail!(\"capsule archive contains duplicate path {name}\");\n        }\n        let entry_type = entry.header().entry_type();\n        if !entry_type.is_dir() && !entry_type.is_file() {\n            bail!(\"capsule archive contains a link or special file {name}\");\n        }\n        let destination = staging.path().join(&path);\n        if entry_type.is_dir() {\n            fs::create_dir_all(&destination)\n                .with_context(|| format!(\"create capsule archive directory {name}\"))?;\n            continue;\n        }\n        if let Some(parent) = destination.parent() {\n            fs::create_dir_all(parent)\n                .with_context(|| format!(\"create capsule archive parent for {name}\"))?;\n        }","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/source_digest.rs#L33-L69","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntar -cf capsule.tar ./src  # built under a non-UTF-8 locale\n// after\nLC_ALL=C.UTF-8 tar -cf capsule.tar ./src","handlingStrategy":"validation","validationCode":"fn all_paths_utf8(archive: &std::path::Path) -> anyhow::Result<()> {\n    let f = std::fs::File::open(archive)?;\n    let mut tar = tar::Archive::new(std::io::BufReader::new(f));\n    for entry in tar.entries()? {\n        let entry = entry?;\n        let p = entry.path()?.to_path_buf();\n        if p.to_str().is_none() {\n            anyhow::bail!(\"non-UTF-8 archive path: {}\", p.display());\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["archive","utf-8","digest"],"backgroundTag":"invalid-argument-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}