{"record":{"id":"d88d83ae7935d3df","repo":"astrid-runtime/astrid","slug":"durable-capsule-id-component-is-missing-from-its","errorCode":null,"errorMessage":"durable capsule {id} component is missing from its archive","messagePattern":"durable capsule (.+?) component is missing from its archive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":299,"sourceCode":"    });\n    if metadata.imports != expected_imports {\n        bail!(\"durable capsule {id} imports differ between metadata and archive\");\n    }\n    let expected_exports = crate::wit::version_map_to_strings(&manifest.exports, |definition| {\n        definition.version.to_string()\n    });\n    if metadata.exports != expected_exports {\n        bail!(\"durable capsule {id} exports differ between metadata and archive\");\n    }\n    if authority.wasm_hash_pinned && metadata.wasm_hash != authority.approved_wasm_hash {\n        bail!(\"durable capsule {id} metadata executable hash differs from authority receipt\");\n    }\n    if let Some(component) = manifest.components.first() {\n        let Some(relative) = component.path.to_str() else {\n            bail!(\"durable capsule {id} component path is not UTF-8\");\n        };\n        let Some(bytes) = archive_files.get(relative) else {\n            bail!(\"durable capsule {id} component is missing from its archive\");\n        };\n        if Path::new(relative)\n            .extension()\n            .is_some_and(|extension| extension.eq_ignore_ascii_case(\"wasm\"))\n        {\n            let archive_hash = blake3::hash(bytes).to_hex().to_string();\n            if authority.wasm_hash_pinned\n                && authority.approved_wasm_hash.as_deref() != Some(archive_hash.as_str())\n            {\n                bail!(\"durable capsule {id} WASM hash differs between authority and archive\");\n            }\n            if metadata.wasm_hash.as_deref() != Some(archive_hash.as_str()) {\n                bail!(\"durable capsule {id} WASM hash differs between metadata and archive\");\n            }\n        } else if metadata.wasm_hash.is_some() {\n            bail!(\"durable capsule {id} metadata names a hash for a non-WASM component\");\n        }\n    } else if metadata.wasm_hash.is_some() {","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L281-L317","documentation":"verify_package_identity looks up the first manifest component's relative path in the archive's file map (archive_files) to hash and verify it. This bail fires when no entry with that exact relative path exists in the archive, i.e. the manifest references a component file that is not actually packaged. The library throws it because the declared executable cannot be verified or executed if it is absent from the archive.","triggerScenarios":"Calling read_verified_durable_package_for_owner on a capsule where manifest.components[0].path (as a UTF-8 string) is not a key in the archive's BTreeMap of files — the manifest was edited/renamed without repacking, or the archive was built without including the component.","commonSituations":"Renaming the component file or its directory in the manifest but shipping the old archive; a packaging script that skips the wasm file (e.g. .gitignore or build output filtering); mismatched manifest and archive versions extracted from different builds; typos in the component path (case-sensitive lookup).","solutions":["Repack the archive so it contains the file at exactly the path given by manifest.components[0].path.","Fix the manifest's component path to match the actual archive entry name (paths are case- and prefix-sensitive).","Reinstall the capsule from a complete, correctly packaged build instead of a partially assembled archive.","List archive entries and diff them against manifest component paths to find the missing/renamed file."],"exampleFix":"// before: manifest points at \"bin/component.wasm\" but archive only has \"component.wasm\"\n// after: align the path when packaging\nlet relative = \"component.wasm\";\nmanifest.components[0].path = PathBuf::from(relative);\narchive_files.insert(relative.to_string(), wasm_bytes);","handlingStrategy":"validation","validationCode":"for component in &manifest.components {\n    let key = component.path.to_str().ok_or_else(|| anyhow!(\"non-UTF-8 path\"))?;\n    anyhow::ensure!(archive_files.contains_key(key), \"archive missing component {key}\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Repack the archive in the same build step that writes the manifest.","Diff archive entry names against manifest component paths in CI packaging tests.","Watch for case-sensitivity and path-prefix drift between manifest and packer."],"tags":["packaging","missing-file","capsule-archive","rust"],"backgroundTag":"file-not-found","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"}