{"record":{"id":"c3073e6788fe4be8","repo":"paperclipai/paperclip","slug":"artifact-source-identity-mismatch","errorCode":null,"errorMessage":"Artifact source identity mismatch.","messagePattern":"Artifact source identity mismatch\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-migrator-artifacts.mjs","lineNumber":34,"sourceCode":"const maximumBytes = 32 * 1024 * 1024;\nconst integrityFor = (bytes) => `sha512-${createHash(\"sha512\").update(bytes).digest(\"base64\")}`;\n\nexport function descriptor(bytes, extension) {\n  const hash = createHash(\"sha512\").update(bytes).digest(\"hex\");\n  return { url: `${artifactBase}/blobs/${hash}.${extension}`, integrity: integrityFor(bytes), size: bytes.length };\n}\n\nfunction assertDescriptor(pin, extension) {\n  if (!pin || typeof pin.integrity !== \"string\" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(pin.integrity) ||\n      !Number.isSafeInteger(pin.size) || pin.size <= 0 || pin.size > maximumBytes) throw new Error(\"Invalid artifact integrity or size.\");\n  const digest = Buffer.from(pin.integrity.slice(7), \"base64\");\n  if (digest.toString(\"base64\") !== pin.integrity.slice(7) || pin.url !== `${artifactBase}/blobs/${digest.toString(\"hex\")}.${extension}`) {\n    throw new Error(\"Artifact URL does not match its content hash and trusted origin.\");\n  }\n}\n\nexport function assertManifest(manifest, sha) {\n  if (manifest?.version !== 1 || manifest.sourceSha !== sha || manifest.packageVersion !== versionFor(sha)) throw new Error(\"Artifact source identity mismatch.\");\n  for (const name of names) assertDescriptor(manifest.packages?.[name], \"tgz\");\n  assertDescriptor(manifest.lockfile, \"json\");\n}\n\nexport function assertLockfile(lock, manifest) {\n  const version = manifest.packageVersion;\n  if (lock?.lockfileVersion !== 3 || !lock.packages || Array.isArray(lock.packages) ||\n      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.\");","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-migrator-artifacts.mjs#L16-L52","documentation":"The cloud migrator artifacts script throws this from assertManifest when the manifest does not correspond to the expected source: version is not 1, sourceSha differs from the provided sha, or packageVersion does not equal versionFor(sha). It binds the artifact set to an exact source revision so you never install artifacts built from different code.","triggerScenarios":"Calling assertManifest(manifest, sha) where manifest.version !== 1, manifest.sourceSha !== sha (checkout/tag mismatch), or manifest.packageVersion !== versionFor(sha) (artifacts published for a different commit).","commonSituations":"Running the migrator after pulling new commits while reusing a cached manifest; a release pipeline that published artifacts for a different SHA; hand-editing packageVersion; using an older manifest format (version !== 1).","solutions":["Regenerate/fetch the manifest for the current source SHA so sourceSha and packageVersion match.","Update your checkout to the SHA the manifest was built from (manifest.sourceSha).","Clear cached/stale manifests from prior runs and re-download.","If the manifest version is not 1, upgrade the consuming tooling to the expected format."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (manifest?.version !== 1) throw new Error('Unsupported manifest version');\nif (manifest.sourceSha !== currentSha) throw new Error(`Manifest is for ${manifest.sourceSha}, checkout is ${currentSha}; re-fetch artifacts`);","typeGuard":"const manifestMatchesSource = (m, sha) => m?.version === 1 && m.sourceSha === sha && typeof m.packageVersion === 'string';","tryCatchPattern":"try { assertManifest(manifest, sha); } catch (e) {\n  if (e.message === 'Artifact source identity mismatch.') {\n    manifest = await fetchManifestFor(sha); // re-download for the current source\n  } else throw e;\n}","preventionTips":["Always fetch the manifest for the exact commit/SHA you are building against.","Invalidate cached manifests when the checkout changes (key caches by SHA).","Pin release pipelines so artifacts, manifest, and SHA are published atomically."],"tags":["artifact","versioning","validation","migration"],"backgroundTag":"incompatible-source-type","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"}