paperclipai/paperclip · error · Error
Migrator dependency has no strong integrity pin.
Error message
Migrator dependency has no strong integrity pin.
What it means
Every non-bundled lockfile entry must carry a strong sha512 integrity pin matching /^sha512-[A-Za-z0-9+/]{86}==$/. This error means an entry's integrity is missing, malformed, or uses a weaker hash, so the artifact cannot guarantee the dependency's bytes are authentic. It is a supply-chain hardening check.
Solutions
- Regenerate the lockfile with the `build` command against registry.npmjs.org so every entry gets a sha512 integrity field
- Check the lockfile is version 3 and entries were not copied from an older sha1-era lockfile
- Do not hand-edit package-lock.json; if an entry lacks integrity, resolve it from the official registry and re-lock
Example fix
// before
"some-dep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/some-dep/-/some-dep-1.0.0.tgz", "integrity": "sha1-abc..." }
// after
"some-dep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/some-dep/-/some-dep-1.0.0.tgz", "integrity": "sha512-<86 base64 chars>==" } Defensive patterns
Strategy: validation
Validate before calling
const STRONG = /^sha512-[A-Za-z0-9+/]{86}==$/;
for (const [key, entry] of Object.entries(lock.packages ?? {})) {
if (key === "" || entry?.inBundle === true) continue;
if (!STRONG.test(entry?.integrity ?? "")) throw new Error(`weak/missing integrity for ${key}`);
} Type guard
const hasStrongIntegrity = (entry) => /^sha512-[A-Za-z0-9+/]{86}==$/.test(entry?.integrity ?? ""); Try / catch
try {
assertLockfile(lock, manifest);
} catch (err) {
if (err.message === "Migrator dependency has no strong integrity pin.") throw new Error("Regenerate the lockfile against registry.npmjs.org to obtain sha512 pins");
throw err;
} Prevention
- Always build against the official registry so npm records sha512 integrity
- Do not mix entries from older sha1-era lockfiles
- Keep lockfileVersion 3
- Treat missing integrity in any lockfile entry as a blocker, not a warning
When it happens
Trigger: assertLockfile sees an entry (outside root and bundled paths) whose integrity is undefined, empty, a sha1 hash, or otherwise not a canonical 86-char base64 sha512 string.
Common situations: Older lockfiles or registries supplying sha1-only integrity; a hand-written or merged lockfile entry lacking integrity; npm config like strict-ssl=false or legacy peer resolution producing weaker pins; lockfileVersion < 3 data mixed in.
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
- Materialized OpenCode executable digest mismatch
- Migrator dependency must resolve to npm.
- Migrator lockfile package pin mismatch.
- ACPX runtime executable digest mismatch
- ACPX private snapshot digest mismatch
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/faf71dc61da08d08.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-migrator-artifacts.mjs:57
export function assertLockfile(lock, manifest) {
const version = manifest.packageVersion;
if (lock?.lockfileVersion !== 3 || !lock.packages || Array.isArray(lock.packages) ||
JSON.stringify(lock.packages[""]?.dependencies) !== JSON.stringify({ "@paperclipai/db": version })) throw new Error("Invalid migrator lockfile root.");
for (const name of names) {
const pin = lock.packages[`node_modules/@paperclipai/${name}`];
const expected = manifest.packages[name];
if (pin?.version !== version || pin.integrity !== expected.integrity || pin.resolved !== expected.url || pin.link || pin.inBundle) throw new Error("Migrator lockfile package pin mismatch.");
}
if (lock.packages["node_modules/@paperclipai/db"].dependencies?.["@paperclipai/shared"] !== version) throw new Error("Migrator shared dependency mismatch.");
for (const [key, entry] of Object.entries(lock.packages)) {
if (key === "") continue;
if (!entry || typeof entry !== "object" || entry.link) throw new Error("Invalid migrator lockfile entry.");
if (/(?:^|\/)node_modules\/@paperclipai\/[^/]+$/.test(key) && !names.some((name) => key === `node_modules/@paperclipai/${name}`)) throw new Error("Unexpected internal migrator dependency.");
if (entry.inBundle === true) {
if (!key.startsWith("node_modules/@paperclipai/db/node_modules/")) throw new Error("Unexpected bundled dependency.");
continue;
}
if (!/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? "")) throw new Error("Migrator dependency has no strong integrity pin.");
if (names.some((name) => key === `node_modules/@paperclipai/${name}`)) continue;
const url = new URL(entry.resolved);
if (url.origin !== "https://registry.npmjs.org" || url.username || url.password || url.search || url.hash) throw new Error("Migrator dependency must resolve to npm.");
}
}
export function buildBundle(directory, sha, { exec = execFileSync } = {}) {
versionFor(sha);
directory = path.resolve(directory);
const packages = {};
for (const name of names) {
const bytes = readFileSync(path.join(directory, `${name}.tgz`));
assertMetadata(tarManifest(bytes), `@paperclipai/${name}`, sha);
packages[name] = descriptor(bytes, "tgz");
}
const scratch = mkdtempSync(path.join(os.tmpdir(), "cloud-migrator-lock-"));
try {
for (const name of names) copyFileSync(path.join(directory, `${name}.tgz`), path.join(scratch, `${name}.tgz`));View on GitHub (pinned to 3f1d897a7c)