astrid-runtime/astrid · error
durable capsule {id} has an unpinned WIT file {key}
Error message
durable capsule {id} has an unpinned WIT file {key} What it means
After verifying all pinned WIT files, verify_wit_files scans the archive for any `wit/*.wit` file that was NOT declared in metadata.wit_files. Durable capsules must pin every WIT file they ship; an unpinned .wit file in the archive means the archive contains interface definitions whose integrity was never attested, so the package is rejected.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:238
{
bail!("durable capsule {id} has unsafe WIT metadata path {relative}");
}
let key = format!("wit/{relative}");
let Some(bytes) = files.get(&key) else {
bail!("durable capsule {id} is missing WIT file {relative}");
};
if !is_hex_digest(pin) || blake3::hash(bytes).to_hex().as_str() != pin {
bail!("durable capsule {id} WIT digest mismatch for {relative}");
}
expected.insert(key);
}
for key in files.keys().filter(|key| key.starts_with("wit/")) {
if Path::new(key)
.extension()
.is_some_and(|extension| extension.eq_ignore_ascii_case("wit"))
&& !expected.contains(key)
{
bail!("durable capsule {id} has an unpinned WIT file {key}");
}
}
Ok(())
}
fn is_hex_digest(value: &str) -> bool {
value.len() == 64
&& value
.bytes()
.all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
}
fn verify_package_identity(
id: &str,
manifest: &CapsuleManifest,
metadata: &CapsuleMeta,
authority: &InstalledAuthority,
manifest_bytes: &[u8],View on GitHub (pinned to affd8760f4)
Solutions
- Regenerate the capsule metadata so wit_files pins every .wit file in the archive, then republish.
- Delete or move stray .wit files out of the archive source tree and rebuild.
- Ensure the packaging step and metadata generation use the same file list (same glob/manifest).
- List untracked files by diffing archive contents (`tar -tzf`) against metadata.wit_files keys.
Example fix
// before: new file added but metadata not regenerated
// archive: wit/api.wit, wit/new.wit; metadata pins only api.wit
// after: regenerate metadata from the archive
metadata.wit_files = pin_all(glob("wit/*.wit")); Defensive patterns
Strategy: validation
Validate before calling
fn no_unpinned_wit_files(archive_files: &[String], pinned: &std::collections::BTreeSet<String>) -> bool {
archive_files.iter().all(|f| {
!f.starts_with("wit/")
|| std::path::Path::new(f).extension().map(|e| e.eq_ignore_ascii_case("wit")) != Some(true)
|| pinned.contains(f)
})
} Type guard
fn is_wit_key(key: &str) -> bool {
key.starts_with("wit/")
&& std::path::Path::new(key).extension().is_some_and(|e| e.eq_ignore_ascii_case("wit"))
} Try / catch
match read_verified_durable_package_for_owner(id) {
Err(e) if e.to_string().contains("unpinned WIT file") => {
// stray .wit in archive: clean the build tree and republish
clean_wit_tree_and_republish(id)?;
}
other => other?,
} Prevention
- Keep the packaging glob and metadata generation in sync (same file list)
- Remove stale .wit files from build trees before archiving
- Regenerate metadata on every archive rebuild, not just on manifest changes
- Diff `tar -tzf` output against metadata.wit_files before publishing
When it happens
Trigger: read_verified_durable_package_for_owner on a capsule whose archive contains extra .wit files under wit/ that metadata.wit_files does not list — e.g. a .wit file added to the archive after metadata generation, or metadata regenerated against a subset of files.
Common situations: Adding a new .wit file and rebuilding the archive but not the metadata; leftover stale .wit files in the build directory that get swept into the archive; packaging scripts that glob wit/*.wit while metadata generation uses a manifest subset.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- durable capsule {id} is missing WIT file {relative}
- durable capsule {} is missing its pinned contracts blob
- durable capsule {id} WIT digest mismatch for {relative}
- durable capsule {id} imports differ between metadata and arc
- opaque capsule asset directories cannot be symlinks: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/c42dcda668cd574b.
Report an issue: GitHub.