{"record":{"id":"3f7b7e9ab83fdd43","repo":"jdx/mise","slug":"packslip-project-version-manifest-digest-diff","errorCode":null,"errorMessage":"packslip:{project}@{version}: manifest digest differs from signed list","messagePattern":"packslip:(.+?)@(.+?): manifest digest differs from signed list","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/backend/packslip.rs","lineNumber":851,"sourceCode":"            ),\n            None => {\n                let vendor = self.locate_bundle(project, &tv, pin, opts).await?;\n                (vendor.url, vendor.digest)\n            }\n        };\n        let url = url.as_str();\n        let text = HTTP_FETCH\n            .get_text_request(url)\n            .headers(&headers_for(url)?)\n            .send()\n            .await?;\n        let actual = hex::encode(Sha256::digest(text.as_bytes()));\n        for expected in vendor_digest\n            .iter()\n            .chain(stamp.and_then(|s| s.digest.as_ref()))\n        {\n            if &actual != expected {\n                bail!(\"packslip:{project}@{version}: manifest digest differs from signed list\");\n            }\n        }\n        let verified = verify_bundle(&text, pin, !opts.allow_unlogged(), &[])?;\n        if verified.project != project || verified.version != version {\n            bail!(\n                \"packslip:{project}@{version}: verified manifest project/version differs from discovery\"\n            );\n        }\n        let scheme = verified.scheme.to_string();\n        let attested_by = verified.attested_by.to_string();\n        packslip_pins::check(\n            project,\n            Observed {\n                scheme: &scheme,\n                key_id: &verified.key_id,\n                issuer: verified.issuer.as_deref(),\n                attested_by: &attested_by,\n                provenance: verified.provenance_linked,","sourceCodeStart":833,"sourceCodeEnd":869,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/backend/packslip.rs#L833-L869","documentation":"During version discovery, mise computes the SHA-256 digest of a downloaded packslip manifest and compares it against every pinned digest — the vendor's signed release list and optionally the mise.lock stamp. If the actual digest differs from ANY pinned digest, install is refused because the manifest is not the artifact that the signing party reviewed. This guards against a swapped or tampered manifest between discovery and install.","triggerScenarios":"Calling latest_version_with_selection_options (candidate_exclusion phase) for a packslip: backend tool when the downloaded manifest bytes hash to a value that does not equal one of the digests in vendor_digest or the stamp's digest — e.g. the upstream release list was updated, the manifest was regenerated/re-signed, or the cache served a stale file.","commonSituations":"A vendor re-published a release with an amended manifest; mise.lock pins an old stamp digest while the vendor list moved on; a proxy or CDN served a different manifest; you edited a packslip manifest locally for testing.","solutions":["Clear the cached manifest and re-run the discovery/install so a fresh copy is fetched","Remove the stale stamp digest entry from mise.lock (and re-trust the current release)","Re-run `mise ls-remote packslip:<project>` to refresh the vendor's signed release list","Verify the vendor's release page for notice of a re-signed manifest before bypassing anything"],"exampleFix":"// before (mise.lock)\n[[tools.node.platforms.linux-x64]]\nstamp.digest = \"old-digest-hex\"\n// after\n# remove the stamp.digest entry or update it to the digest published in the vendor's signed release list","handlingStrategy":"validation","validationCode":"use sha2::{Digest, Sha256};\nfn manifest_matches_pinned(manifest_bytes: &[u8], pinned: &[String]) -> bool {\n    let actual = hex::encode(Sha256::digest(manifest_bytes));\n    pinned.iter().any(|p| p.eq_ignore_ascii_case(&actual))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Don't hand-edit mise.lock stamp digests; let mise write them","After a vendor re-signs a release, refresh the lock entry instead of keeping the old digest","Keep caches clean on CI so stale manifests aren't reused"],"tags":["packslip","sha256","integrity","manifest"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}