paperclipai/paperclip · error · Error
Migrator lockfile package pin mismatch.
Error message
Migrator lockfile package pin mismatch.
What it means
assertLockfile validates that the generated npm lockfile pins the internal @paperclipai/db and @paperclipai/shared packages to the exact version, integrity, and resolved URL recorded in the artifact manifest. This error is thrown when a node_modules/@paperclipai/<name> entry disagrees with the manifest on version, integrity hash, resolved URL, or is a link/bundled package. It is an internal supply-chain invariant: the lockfile must reference exactly the tgz artifacts whose bytes were verified.
Solutions
- Re-run `node scripts/cloud-migrator-artifacts.mjs build <dir> <sha>` so the lockfile is regenerated from the current tarballs and the resolved URLs/integrity are rewritten to match the manifest
- Check that manifest.json and package-lock.json in the bundle directory come from the same build (same source SHA and package version)
- Do not run `npm install`/`npm update` against the bundle directory after building; that re-resolves pins
- Diff the offending node_modules/@paperclipai/<name> entry against manifest.packages[name] to see which field (version, integrity, resolved) diverged
Example fix
// before (lockfile drifted from manifest)
"node_modules/@paperclipai/db": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@paperclipai/db/-/db-0.4.1.tgz", ... }
// after (regenerated by buildBundle; points at immutable CDN blob)
"node_modules/@paperclipai/db": { "version": "0.4.2", "resolved": "https://d1p6rlowie26tp.cloudfront.net/cloud-migrators/v1/blobs/<sha512hex>.tgz", "integrity": "sha512-..." } Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from "node:fs";
const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
const manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
for (const name of ["db", "shared"]) {
const pin = lock.packages?.[`node_modules/@paperclipai/${name}`];
const exp = manifest.packages[name];
if (pin?.version !== manifest.packageVersion || pin?.integrity !== exp.integrity || pin?.resolved !== exp.url)
throw new Error(`lockfile pin for @paperclipai/${name} drifts from manifest`);
} Type guard
const pinMatches = (lock, manifest, name) => {
const pin = lock?.packages?.[`node_modules/@paperclipai/${name}`];
const exp = manifest?.packages?.[name];
return !!pin && pin.version === manifest.packageVersion && pin.integrity === exp.integrity && pin.resolved === exp.url && !pin.link && !pin.inBundle;
}; Try / catch
try {
assertLockfile(lock, manifest);
} catch (err) {
if (err.message === "Migrator lockfile package pin mismatch.") {
// regenerate the lockfile from the verified tarballs
buildBundle(dir, sha);
} else throw err;
} Prevention
- Never hand-edit package-lock.json after buildBundle; always regenerate via the build command
- Always build manifest.json and package-lock.json in the same buildBundle run
- Do not run npm install/update inside the bundle directory after building
- Diff lock pins against the manifest before publishing (validate command does this)
When it happens
Trigger: assertLockfile(lock, manifest) is called with a lockfile whose node_modules/@paperclipai/db or node_modules/@paperclipai/shared entry has a version !== manifest.packageVersion, an integrity or resolved URL differing from manifest.packages[name], or truthy link/inBundle flags.
Common situations: Hand-editing or regenerating package-lock.json after buildBundle rewrote the resolved URLs; npm re-resolution picking up a newly published npm version of @paperclipai/db instead of the local tarball; a stale lockfile from an older package version being validated against a new manifest; copying the lock between builds.
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
- Invalid migrator lockfile entry.
- Migrator dependency has no strong integrity pin.
- Unexpected internal migrator dependency.
- Artifact bytes do not match their immutable pin.
- Invalid artifact integrity or size.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/7d290bfdf882ff84.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-migrator-artifacts.mjs:46
if (digest.toString("base64") !== pin.integrity.slice(7) || pin.url !== `${artifactBase}/blobs/${digest.toString("hex")}.${extension}`) {
throw new Error("Artifact URL does not match its content hash and trusted origin.");
}
}
export function assertManifest(manifest, sha) {
if (manifest?.version !== 1 || manifest.sourceSha !== sha || manifest.packageVersion !== versionFor(sha)) throw new Error("Artifact source identity mismatch.");
for (const name of names) assertDescriptor(manifest.packages?.[name], "tgz");
assertDescriptor(manifest.lockfile, "json");
}
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 } = {}) {View on GitHub (pinned to 3f1d897a7c)