{"record":{"id":"fb59569c3948b559","repo":"astrid-runtime/astrid","slug":"durable-capsule-is-missing-its-pinned-contracts","errorCode":null,"errorMessage":"durable capsule {} is missing its pinned contracts blob","messagePattern":"durable capsule (.+?) is missing its pinned contracts blob","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/contracts.rs","lineNumber":117,"sourceCode":"        if !is_blake3_pin(pin) {\n            bail!(\n                \"durable capsule {} has malformed contracts pin\",\n                summary.id()\n            );\n        }\n        let Some(relative) = package\n            .metadata()\n            .wit_files\n            .keys()\n            .filter(|relative| {\n                Path::new(relative.as_str())\n                    .file_name()\n                    .and_then(|name| name.to_str())\n                    == Some(CONTRACTS_WIT_BASENAME)\n            })\n            .min()\n        else {\n            bail!(\n                \"durable capsule {} is missing its pinned contracts blob\",\n                summary.id()\n            );\n        };\n        let Some(blob) = package.wit_file(relative) else {\n            bail!(\n                \"durable capsule {} is missing its pinned contracts blob\",\n                summary.id()\n            );\n        };\n        if blake3::hash(blob).to_hex().as_str() != pin {\n            bail!(\n                \"durable capsule {} contracts blob digest mismatch\",\n                summary.id()\n            );\n        }\n        let count = counts.entry(pin.clone()).or_default();\n        *count = count.saturating_add(1);","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/contracts.rs#L99-L135","documentation":"After validating the pin, durable_contracts_pin looks up the relative path of the astrid-contracts.wit entry whose basename matches the pin key. If no entry's basename matches CONTRACTS_WIT_BASENAME, the capsule is considered internally inconsistent — it claims to pin contracts (contracts_pin returned Some) but the code cannot find the matching relative path — so the scan fails fast instead of skipping the capsule.","triggerScenarios":"Calling durable_contracts_pin / refresh_canonical_contracts_from_registry when a capsule's meta.json has an astrid-contracts.wit pin entry, yet the filter over wit_files keys (matching on Path::file_name == astrid-contracts.wit) unexpectedly yields no key — e.g. a non-UTF8 or unusual path encoding case that dodges the first lookup — an internal-inconsistency state reached when contracts_pin succeeds but the second key enumeration fails.","commonSituations":"meta.json whose wit_files keys were rewritten with different casing or extensions; a buggy custom packager writing pin entries without matching key paths.","solutions":["Reinstall or rebuild the offending capsule so meta.json's wit_files contains a key whose basename is exactly `astrid-contracts.wit`.","Inspect the capsule's meta.json wit_files keys and correct the entry basename to astrid-contracts.wit.","Re-publish the durable package if its metadata cannot be repaired in place."],"exampleFix":"// before (meta.json wit_files key)\n\"deps/astrid-contracts/contracts.wit\": \"<blake3 hex>\"\n// after\n\"deps/astrid-contracts/astrid-contracts.wit\": \"<blake3 hex>\"","handlingStrategy":"validation","validationCode":"// pre-check before scanning:\nlet pin_key = package.metadata().wit_files.keys()\n    .find(|k| Path::new(k).file_name().and_then(|n| n.to_str()) == Some(\"astrid-contracts.wit\"));\nassert!(pin_key.is_some(), \"capsule lacks astrid-contracts.wit entry\");","typeGuard":"fn has_contracts_entry(wit_files: &HashMap<String, String>) -> bool {\n    wit_files.keys().any(|k| Path::new(k).file_name().and_then(|n| n.to_str()) == Some(\"astrid-contracts.wit\"))\n}","tryCatchPattern":null,"preventionTips":["Keep wit_files keys' basenames exactly `astrid-contracts.wit` when packaging.","Use the SDK packager instead of custom metadata writers.","Validate archives after publish with the packaging tool's verify step."],"tags":["rust","metadata","wit","integrity"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}