{"record":{"id":"96264bd7991440f5","repo":"paperclipai/paperclip","slug":"migrator-dependency-must-resolve-to-npm","errorCode":null,"errorMessage":"Migrator dependency must resolve to npm.","messagePattern":"Migrator dependency must resolve to npm\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-migrator-artifacts.mjs","lineNumber":60,"sourceCode":"      JSON.stringify(lock.packages[\"\"]?.dependencies) !== JSON.stringify({ \"@paperclipai/db\": version })) throw new Error(\"Invalid migrator lockfile root.\");\n  for (const name of names) {\n    const pin = lock.packages[`node_modules/@paperclipai/${name}`];\n    const expected = manifest.packages[name];\n    if (pin?.version !== version || pin.integrity !== expected.integrity || pin.resolved !== expected.url || pin.link || pin.inBundle) throw new Error(\"Migrator lockfile package pin mismatch.\");\n  }\n  if (lock.packages[\"node_modules/@paperclipai/db\"].dependencies?.[\"@paperclipai/shared\"] !== version) throw new Error(\"Migrator shared dependency mismatch.\");\n  for (const [key, entry] of Object.entries(lock.packages)) {\n    if (key === \"\") continue;\n    if (!entry || typeof entry !== \"object\" || entry.link) throw new Error(\"Invalid migrator lockfile entry.\");\n    if (/(?:^|\\/)node_modules\\/@paperclipai\\/[^/]+$/.test(key) && !names.some((name) => key === `node_modules/@paperclipai/${name}`)) throw new Error(\"Unexpected internal migrator dependency.\");\n    if (entry.inBundle === true) {\n      if (!key.startsWith(\"node_modules/@paperclipai/db/node_modules/\")) throw new Error(\"Unexpected bundled dependency.\");\n      continue;\n    }\n    if (!/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? \"\")) throw new Error(\"Migrator dependency has no strong integrity pin.\");\n    if (names.some((name) => key === `node_modules/@paperclipai/${name}`)) continue;\n    const url = new URL(entry.resolved);\n    if (url.origin !== \"https://registry.npmjs.org\" || url.username || url.password || url.search || url.hash) throw new Error(\"Migrator dependency must resolve to npm.\");\n  }\n}\n\nexport function buildBundle(directory, sha, { exec = execFileSync } = {}) {\n  versionFor(sha);\n  directory = path.resolve(directory);\n  const packages = {};\n  for (const name of names) {\n    const bytes = readFileSync(path.join(directory, `${name}.tgz`));\n    assertMetadata(tarManifest(bytes), `@paperclipai/${name}`, sha);\n    packages[name] = descriptor(bytes, \"tgz\");\n  }\n  const scratch = mkdtempSync(path.join(os.tmpdir(), \"cloud-migrator-lock-\"));\n  try {\n    for (const name of names) copyFileSync(path.join(directory, `${name}.tgz`), path.join(scratch, `${name}.tgz`));\n    const root = { name: \"paperclip-migrator-install-root\", version: \"0.0.0\", private: true,\n      dependencies: { \"@paperclipai/db\": \"file:db.tgz\", \"@paperclipai/shared\": \"file:shared.tgz\" } };\n    writeFileSync(path.join(scratch, \"package.json\"), JSON.stringify(root));","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-migrator-artifacts.mjs#L42-L78","documentation":"Non-internal, non-bundled lockfile entries must resolve from the official npm registry: the resolved URL's origin must be https://registry.npmjs.org with no credentials, query, or fragment. This error fires when a dependency resolves from a mirror, git URL, file path, or a URL carrying credentials/query strings — any of which would bypass the trusted-origin guarantee.","triggerScenarios":"assertLockfile computes new URL(entry.resolved) and the origin is not https://registry.npmjs.org, or username/password/search/hash are non-empty (e.g. resolved pointing at a GitHub tarball, a private registry mirror, or a URL like ...tgz?cache=bust).","commonSituations":"Building on a machine configured with a corporate npm mirror (.npmrc registry=...), a dependency specified as a git/file URL in package.json, or a proxy injecting query parameters into resolved URLs.","solutions":["Set the registry to the official one for the build: run npm with --registry=https://registry.npmjs.org (as buildBundle does) and neutralize project .npmrc overrides","Replace git:/file:/http: dependency specs in db/shared package.json with published npm versions, then re-lock","Strip any credentials/query from resolved URLs by regenerating the lockfile rather than editing entries"],"exampleFix":"// before (built against mirror)\n\"some-dep\": { \"resolved\": \"https://npm.internal.corp/some-dep/-/some-dep-1.0.0.tgz\" }\n// after (built with --registry=https://registry.npmjs.org)\n\"some-dep\": { \"resolved\": \"https://registry.npmjs.org/some-dep/-/some-dep-1.0.0.tgz\", \"integrity\": \"sha512-...\" }","handlingStrategy":"validation","validationCode":"const Official = \"https://registry.npmjs.org\";\nfor (const [key, entry] of Object.entries(lock.packages ?? {})) {\n  if (key === \"\" || entry?.inBundle === true) continue;\n  if (/node_modules\\/@paperclipai\\/(db|shared)$/.test(key)) continue;\n  const u = new URL(entry.resolved);\n  if (u.origin !== Official || u.username || u.password || u.search || u.hash)\n    throw new Error(`dependency ${key} resolves off official npm: ${entry.resolved}`);\n}","typeGuard":"const resolvesToOfficialNpm = (entry) => {\n  try { const u = new URL(entry?.resolved); return u.origin === \"https://registry.npmjs.org\" && !u.username && !u.password && !u.search && !u.hash; }\n  catch { return false; }\n};","tryCatchPattern":"try {\n  assertLockfile(lock, manifest);\n} catch (err) {\n  if (err.message === \"Migrator dependency must resolve to npm.\") throw new Error(\"Rebuild with --registry=https://registry.npmjs.org and without mirror/git deps\");\n  throw err;\n}","preventionTips":["Run builds with --registry=https://registry.npmjs.org and audit project .npmrc for mirror overrides","Avoid git:/file:/URL dependency specs in db and shared package.json","Regenerate (never hand-edit) resolved URLs","Run `validate` in CI so off-registry resolution is caught before publish"],"tags":["supply-chain","npm-registry","lockfile","security"],"backgroundTag":"invalid-url","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}