paperclipai/paperclip · error · Error
Invalid migrator lockfile entry.
Error message
Invalid migrator lockfile entry.
What it means
Every non-root lockfile entry must be a well-formed package object and must not be a link (entry.link truthy). This error means a packages[] entry is null, not an object, or is a filesystem link entry, so its provenance cannot be verified. The validator refuses any entry it cannot pin by integrity.
Solutions
- Remove link/file: dependency styles from the migrator install root and rebuild so npm produces real registry/tarball entries
- Regenerate the lockfile with the `build` command instead of hand-editing package-lock.json
- Validate the lockfile JSON is not truncated or merge-corrupted (JSON.parse + inspect packages entries)
Example fix
// before
"node_modules/foo": { "link": true, "resolved": "packages/foo" }
// after (rebuild without link deps)
"node_modules/foo": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/foo/-/foo-1.2.3.tgz", "integrity": "sha512-..." } Defensive patterns
Strategy: validation
Validate before calling
for (const [key, entry] of Object.entries(lock.packages ?? {})) {
if (key === "") continue;
if (!entry || typeof entry !== "object" || entry.link) throw new Error(`bad lockfile entry: ${key}`);
} Type guard
const isSolidEntry = (entry) => !!entry && typeof entry === "object" && !entry.link;
Try / catch
try {
assertLockfile(lock, manifest);
} catch (err) {
if (err.message === "Invalid migrator lockfile entry.") {
// find and report the offending key
for (const [k, e] of Object.entries(lock.packages)) if (!e || typeof e !== "object" || e.link) console.error(`offending entry: ${k}`);
}
throw err;
} Prevention
- Avoid file:/workspace link dependencies in the migrator install root
- Never hand-merge package-lock.json files; regenerate instead
- JSON.parse the lockfile and sanity-check entries before validating
- Keep lockfileVersion 3 (modern npm) so entry shapes are uniform
When it happens
Trigger: assertLockfile(lock, manifest) iterates lock.packages and hits an entry that is null/undefined/a non-object, or one with a truthy link property (e.g. a file: or workspace link entry npm recorded).
Common situations: Installing with workspaces or file: link dependencies so npm emits link entries in the lockfile; a corrupted or truncated package-lock.json; a hand-merged lockfile leaving a null entry.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Migrator lockfile package pin mismatch.
- Unexpected internal migrator dependency.
- Invalid migrator lockfile root.
- Migrator dependency has no strong integrity pin.
- Migrator dependency must resolve to npm.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/d2bf9eca51526eb6.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-migrator-artifacts.mjs:51
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 } = {}) {
versionFor(sha);
directory = path.resolve(directory);
const packages = {};
for (const name of names) {
const bytes = readFileSync(path.join(directory, `${name}.tgz`));View on GitHub (pinned to 3f1d897a7c)