midudev/autoskills · critical · Error
bundle hash mismatch
Error message
bundle hash mismatch
What it means
After downloading all files of a registry entry, the installer computes a deterministic bundle hash from each file's relative path and content hash, and compares it to the bundleHash published in the registry entry. A mismatch means the downloaded content differs from what the registry publisher hashed — either the files changed on the source branch or the manifest hash is stale/wrong.
Solutions
- Re-run the install to fetch from the version-pinned URL rather than the 'main' fallback (ensure the package version resolves and the version tag exists upstream).
- Regenerate the registry manifest (recompute bundleHash and per-file sha256) if you are the registry publisher and legitimately changed files.
- Re-sync your local skills-registry.json from the trusted source in case the manifest copy is stale.
- Investigate the source branch for unexpected edits if the change was not intentional (possible tampering).
Example fix
// before (registry main branch edited after release) files fetched from /main/... -> bundle hash mismatch // after (pin and republish) # republish: recompute bundleHash in skills-registry.json at tag vX.Y.Z $ pnpm run registry:publish
Defensive patterns
Strategy: try-catch
Validate before calling
import { createHash, createHmac } from "node:crypto";
function bundleHashOf(files) {
return createHash("sha256")
.update(files.map(({ rel, buf }) => `${rel}:${createHash("sha256").update(buf).digest("hex")}`).sort().join("\n")).digest("hex");
}
// verify only after fetching over a trusted channel; never skip the check Try / catch
try {
await downloadRegistryEntryToCache(entry, cacheDir);
} catch (e) {
if (e.message === "bundle hash mismatch") {
quarantineCache(cacheDir); // discard unverified content
throw new Error("Registry content failed integrity check; refusing to install. Re-sync the registry manifest.");
} else throw e;
} Prevention
- Install from version-pinned registry URLs instead of the mutable main fallback.
- Regenerate bundleHash with the official publish script whenever upstream files change.
- Re-sync the local manifest before installs; never disable the hash check.
- Investigate upstream edits when a mismatch appears unexpectedly — it can indicate tampering.
When it happens
Trigger: downloadRegistryEntry downloads files from the mutable 'main' fallback URL (or any base whose content differs from the pinned version), producing a bundle hash different from entry.bundleHash in the registry manifest.
Common situations: Downloading from the /main/ fallback after files were edited upstream but the registry manifest wasn't regenerated; a partially updated registry release; tampered registry or man-in-the-middle-modified content; hand-edited skill files upstream.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
AI-assisted analysis of midudev/autoskills@0ec725320d (2026-09-15).
Data as JSON: /api/errors/5170d0385233d66d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/autoskills/installer.ts:347
): Promise<void> {
const files = [];
for (const rel of entry.files) {
files.push({
rel: normalizeRegistryRelPath(rel),
...(await downloadRegistryFile(skillName, entry, rel, opts)),
});
}
const bundleHash = createHash("sha256")
.update(
files
.map(({ rel, buf }) => `${rel}:${sha256Buffer(buf)}`)
.sort()
.join("\n"),
)
.digest("hex");
if (bundleHash !== entry.bundleHash) {
throw new Error("bundle hash mismatch");
}
rmSync(destDir, { recursive: true, force: true });
for (const { rel, buf } of files) {
const dest = join(destDir, ...rel.split("/"));
mkdirSync(dirname(dest), { recursive: true });
writeFileSync(dest, buf);
}
opts.onTrace?.(`wrote downloaded bundle to ${destDir}`);
}
function copyRegistryEntryFromLocal(
skillName: string,
entry: RegistryEntry,
destDir: string,
opts: InstallOptions,
): boolean {
const registryDir = getInstallRegistryDir(opts);View on GitHub (pinned to 0ec725320d)