{"record":{"id":"5de09b203199c360","repo":"jdx/mise","slug":"name-sha256-is-actual-the-packslip-says","errorCode":null,"errorMessage":"{name}: sha256 is {actual}, the packslip says {}","messagePattern":"(.+?): sha256 is (.+?), the packslip says (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packslip.rs","lineNumber":371,"sourceCode":"                    let Some(url) = &resource.url else {\n                        warn!(\"{}: asset {name} has no download URL\", tv.style());\n                        continue;\n                    };\n                    pr.set_message(format!(\"download {name}\"));\n                    file::create_dir_all(dest.parent().unwrap_or(&base))?;\n                    // The tool is installed by now and the asset is an extra:\n                    // one that cannot be fetched is reported, not fatal. One\n                    // that arrives with the wrong digest is another matter.\n                    if let Err(err) = download_file(url, &dest, Some(pr)).await {\n                        let _ = file::remove_all(&dest);\n                        warn!(\"{}: could not fetch {name}: {err}\", tv.style());\n                        continue;\n                    }\n                    let (actual, _) = packslip::digest_file(&dest)?;\n                    let expected = statement.digest_of(name);\n                    if expected != Some(actual.as_str()) {\n                        let _ = file::remove_all(&dest);\n                        bail!(\n                            \"{name}: sha256 is {actual}, the packslip says {}\",\n                            expected.unwrap_or(\"it is not a subject\")\n                        );\n                    }\n                }\n                // The archive and the unpacked skill are separate: an archive\n                // left by an earlier attempt still needs unpacking.\n                if resource.kind == \"skill\"\n                    && let Some(skill) = skill_name(resource)\n                {\n                    let dir = base.join(\"skills\").join(skill);\n                    // Like the other skill sources: a skill that cannot be\n                    // unpacked is reported, and the tool still installs. The\n                    // digest check above stays fatal.\n                    if !dir.join(\"SKILL.md\").is_file()\n                        && let Err(err) = unpack_skill(&dest, &dir, pr)\n                    {\n                        warn!(\"{}: could not unpack skill {skill}: {err}\", tv.style());","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/packslip.rs#L353-L389","documentation":"fetch_files verifies each downloaded subject against the sha256 recorded in the packslip manifest. When a file's actual digest differs from — or is absent from — the manifest, the downloaded file is deleted and this error is raised to prevent installing tampered or mismatched content.","triggerScenarios":"The archive/skill downloaded from the URL hashes to a different value than statement.digest_of(name) expects, or the packslip lists no digest for the file it landed on (name is not a subject).","commonSituations":"Upstream re-released a file under the same URL (digest changed), packslip is stale relative to the project's release, a proxy/mirror served modified content, a filename in the packslip changed so no expected digest exists.","solutions":["Refresh/regenerate the packslip so its digests match the current upstream release","Check whether the upstream release was updated and re-pin to the intended version","Verify your network path (proxy/mirror) is not altering downloads","If the file legitimately is not a subject, correct the packslip entry so its name/digest mapping matches"],"exampleFix":"// before (packslip.toml)\n// [[subject]] name = \"skill.tar.gz\" sha256 = \"aaa...\"\n// after: update to the digest of the file actually published\n// [[subject]] name = \"skill.tar.gz\" sha256 = \"bbb...\"  # sha256sum skill.tar.gz","handlingStrategy":"validation","validationCode":"// compare digests before installing\nconst expected = statement.digestOf(name);\nconst actual = await sha256File(dest);\nif (expected && expected !== actual) throw new Error(`${name}: digest mismatch`);","typeGuard":null,"tryCatchPattern":"try { await fetchFiles(...) } catch (e) { if (/sha256 is .*the packslip says/.test(e.message)) { refreshPackslip(); } else { throw e; } }","preventionTips":["Regenerate the packslip whenever upstream re-releases a file","Record sha256 with sha256sum into the packslip","Pin to immutable release asset URLs","Investigate proxies/mirrors that may alter content"],"tags":["packslip","checksum","security","download"],"backgroundTag":"checksum-mismatch","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}