{"record":{"id":"5ff3cdead0a9e534","repo":"Hmbown/CodeWhale","slug":"expected-revision-must-be-an-exact-40-character-gi","errorCode":null,"errorMessage":"expected revision must be an exact 40-character Git SHA","messagePattern":"expected revision must be an exact 40-character Git SHA","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/compare-deployed-facts.mjs","lineNumber":113,"sourceCode":"  }\n  return differences;\n}\n\nasync function main() {\n  const args = parseArgs(process.argv.slice(2));\n  let baseUrl;\n  try {\n    baseUrl = new URL(args.baseUrl);\n  } catch {\n    throw new Error(`invalid --base-url: ${args.baseUrl}`);\n  }\n  if (!/^https?:$/.test(baseUrl.protocol)) {\n    throw new Error(\"--base-url must use http or https\");\n  }\n\n  const expectedRevision = args.expectedRevision || localRevision();\n  if (!expectedRevision || !/^[0-9a-f]{40}$/i.test(expectedRevision)) {\n    throw new Error(\"expected revision must be an exact 40-character Git SHA\");\n  }\n  if (!Number.isInteger(args.attempts) || args.attempts < 1 || args.attempts > 20) {\n    throw new Error(\"--attempts must be an integer from 1 to 20\");\n  }\n  if (!Number.isFinite(args.retryDelayMs) || args.retryDelayMs < 0 || args.retryDelayMs > 30_000) {\n    throw new Error(\"--retry-delay-ms must be between 0 and 30000\");\n  }\n\n  const facts = buildFacts();\n  const expected = {\n    sourceRevision: expectedRevision,\n    version: facts.version,\n    providerCount: facts.providers.length,\n    toolCount: facts.toolCount,\n    latestPublishedRelease: facts.latestPublishedRelease,\n  };\n  const endpoint = new URL(\"/api/facts\", baseUrl).toString();\n  let last = { error: \"not attempted\" };","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/web/scripts/compare-deployed-facts.mjs#L95-L131","documentation":"The expected revision comes from --expected-revision or from local git HEAD via execFileSync('git', ['rev-parse', 'HEAD']). It must match /^[0-9a-f]{40}$/i — a full 40-character commit SHA — because it is compared against the deployed build's sourceRevision. Short SHAs, tags, branch names, or a null from a missing/unusable git repo all fail with this message.","triggerScenarios":"Passing --expected-revision with a short SHA (abc1234) or a tag (v1.2.3); omitting the flag outside a git checkout, in a shallow or broken repo, or where git is not installed.","commonSituations":"Pasting the abbreviated SHA GitHub displays; running the script from an exported tarball; a minimal container without git.","solutions":["Expand to the full SHA: `git rev-parse <short>` and pass the 40-char result","If omitting the flag, run from a healthy repo checkout with git on PATH","In CI, pass the exact commit — --expected-revision \"$GITHUB_SHA\" is already 40 chars"],"exampleFix":"# before\n--expected-revision v1.2.3\n# after\n--expected-revision $(git rev-parse HEAD)","handlingStrategy":"validation","validationCode":"const SHA_RE = /^[0-9a-f]{40}$/i;\nconst revision = args.expectedRevision || (await localRevision());\nif (!SHA_RE.test(String(revision || ''))) {\n  console.error('expected revision must be an exact 40-character Git SHA');\n  process.exit(2);\n}","typeGuard":"function isFullGitSha(value) {\n  return /^[0-9a-f]{40}$/i.test(String(value || ''));\n}","tryCatchPattern":"try {\n  await main();\n} catch (error) {\n  if (/exact 40-character Git SHA/.test(error.message)) { console.error('Pass --expected-revision $(git rev-parse HEAD)'); process.exit(2); }\n  throw error;\n}","preventionTips":["Expand short SHAs with `git rev-parse` before passing them","In CI, feed the exact commit SHA the workflow checked out (e.g. $GITHUB_SHA)"],"tags":["git","validation","cli","web"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}