{"record":{"id":"b51612fc96ece811","repo":"paperclipai/paperclip","slug":"an-immutable-readme-asset-ref-is-required","errorCode":null,"errorMessage":"an immutable README asset ref is required","messagePattern":"an immutable README asset ref is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/prepare-npm-readme.mjs","lineNumber":7,"sourceCode":"import { readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport function prepareNpmReadme(readme, assetRef) {\n  if (!assetRef) {\n    throw new Error(\"an immutable README asset ref is required\");\n  }\n\n  const assetBaseUrl =\n    `https://raw.githubusercontent.com/paperclipai/paperclip/${assetRef}/doc/assets/`;\n\n  return readme.replace(\n    /((?:src|srcset)=[\"'])([^\"']*)([\"'])/g,\n    (_match, prefix, value, suffix) =>\n      `${prefix}${value.replace(\n        /(^|,\\s*)doc\\/assets\\//g,\n        `$1${assetBaseUrl}`,\n      )}${suffix}`,\n  );\n}\n\nconst isDirectRun =\n  process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/prepare-npm-readme.mjs#L1-L25","documentation":"prepareNpmReadme() rewrites doc asset links in the README to point at an immutable git ref (commit SHA or tag) on raw.githubusercontent.com, so the npm-published README never references moving resources like 'main'. It refuses to run without an explicit assetRef because publishing relative/mutable links would break asset rendering on npm.","triggerScenarios":"Calling prepareNpmReadme(readme, assetRef) with assetRef === \"\" / undefined / null — e.g. the CLI wrapper invoked with only two arguments, or a release script computing the ref from an empty git describe output.","commonSituations":"A release pipeline step that forgets to pass the commit SHA/tag; a git command that resolves to an empty string (detached HEAD quirks, shallow clone without tags) feeding an empty ref into the script; manually running the module from another script without the third argument.","solutions":["Pass an immutable ref (commit SHA or release tag) as the third CLI argument or second function argument, e.g. node scripts/prepare-npm-readme.mjs README.md dist/README.md abc1234.","In the release script, resolve the ref with git rev-parse HEAD (or the tag) and fail early if empty before invoking prepareNpmReadme.","If running programmatically, guard the call: if (!assetRef) throw before calling, to get a clearer stack at the call site."],"exampleFix":"// before\nprepareNpmReadme(readme, process.env.GIT_REF ?? \"\")\n// after\nconst ref = execSync(\"git rev-parse HEAD\").toString().trim();\nif (!ref) throw new Error(\"cannot resolve immutable asset ref\");\nprepareNpmReadme(readme, ref)","handlingStrategy":"try-catch","validationCode":"const ref = process.argv[2];\nif (!ref || ref.trim() === '') throw new Error('prepareNpmReadme: pass an immutable commit SHA or tag as assetRef');","typeGuard":"const isImmutableRef = (r) => typeof r === 'string' && /^[0-9a-f]{40}$|^(v?\\d+\\.\\d+\\.\\d+)$/.test(r.trim());","tryCatchPattern":"try { const out = prepareNpmReadme(readme, ref); } catch (e) { if (e.message.includes('immutable README asset ref')) { console.error('Resolve the release SHA/tag first: git rev-parse HEAD'); process.exit(1); } throw e; }","preventionTips":["Resolve the ref from git (git rev-parse HEAD) in the release script, never from free-form env vars.","Fail the release pipeline early if the ref resolves to an empty string.","Prefer full commit SHAs or release tags over branch names for asset pinning."],"tags":["npm","readme","release","missing-argument"],"backgroundTag":"missing-required-argument","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"}