{"record":{"id":"fdd7a27adaea1127","repo":"paperclipai/paperclip","slug":"invalid-artifact-integrity-or-size","errorCode":null,"errorMessage":"Invalid artifact integrity or size.","messagePattern":"Invalid artifact integrity or size\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-migrator-artifacts.mjs","lineNumber":26,"sourceCode":"import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { assertMetadata, tarManifest, versionFor } from \"./preview-artifacts.mjs\";\n\nexport const artifactBase = \"https://d1p6rlowie26tp.cloudfront.net/cloud-migrators/v1\";\nexport const artifactBucket = \"paperclipai-runner-e2e-history-078455283791-us-east-1\";\nconst prefix = \"cloud-migrators/v1/\";\nconst names = [\"db\", \"shared\"];\nconst 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}`];","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-migrator-artifacts.mjs#L8-L44","documentation":"The cloud migrator artifacts script throws this from assertDescriptor when an artifact pin fails structural validation: integrity is not a sha512 base64 string of the exact form sha512-<86 chars>==, size is not a safe positive integer within maximumBytes, or the pin itself is missing. It guards against trusting malformed artifact descriptors before any download.","triggerScenarios":"Calling assertDescriptor with a pin object that is null/undefined, has a malformed or non-string integrity, an integer-unsafe/negative/oversized size, or (implicitly) a pin produced by an older script version with a different integrity format.","commonSituations":"A hand-edited or corrupted manifest JSON; an artifact descriptor generated by an incompatible tool version; truncation of the integrity string during copy/paste; a size field recorded as a string.","solutions":["Regenerate the artifact manifest with the current scripts/cloud-migrator-artifacts.mjs so integrity/size are recomputed correctly.","Verify pin.integrity matches /^sha512-[A-Za-z0-9+/]{86}==$/ and pin.size is a positive integer within maximumBytes.","Ensure you are loading the intended manifest file and it was not truncated or hand-edited.","If size exceeds maximumBytes, reduce the artifact size or adjust the trusted maximum intentionally."],"exampleFix":"// before\n\"integrity\": \"sha512-abc123\", \"size\": \"4096\"\n// after\n\"integrity\": \"sha512-<86 base64 chars>==\", \"size\": 4096","handlingStrategy":"validation","validationCode":"const okIntegrity = (i) => typeof i === 'string' && /^sha512-[A-Za-z0-9+/]{86}==$/.test(i);\nconst okSize = (s) => Number.isSafeInteger(s) && s > 0 && s <= maximumBytes;\nif (!okIntegrity(pin?.integrity) || !okSize(pin?.size)) throw new Error('pin malformed before use');","typeGuard":"const isWellFormedPin = (p) => p != null && typeof p.integrity === 'string' && /^sha512-[A-Za-z0-9+/]{86}==$/.test(p.integrity) && Number.isSafeInteger(p.size) && p.size > 0;","tryCatchPattern":"try { assertDescriptor(pin, 'tgz'); } catch (e) {\n  if (e.message === 'Invalid artifact integrity or size.') {\n    console.error('Regenerate the artifact manifest with the current migrator script');\n  } else throw e;\n}","preventionTips":["Never hand-edit integrity or size fields; always regenerate via the script.","Copy integrity strings programmatically, not by hand, to avoid truncation.","Keep sizes under the trusted maximumBytes limit; split oversized artifacts."],"tags":["integrity","validation","artifact","sha512"],"backgroundTag":"checksum-mismatch","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"}