{"record":{"id":"5ff3cdead0a9e534","repo":"Hmbown/CodeWhale","slug":"expected-revision-must-be-an-exact-40-character-git-sha","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/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/scripts/compare-deployed-facts.mjs#L95-L131","documentation":"The script requires the expected source revision to be a full 40-character hexadecimal Git SHA. It takes --expected-revision if given, otherwise derives it from the local checkout via localRevision(); either way the value must match /^[0-9a-f]{40}$/i or the script aborts so it never compares against an ambiguous or wrong revision.","triggerScenarios":"Passing --expected-revision with a short SHA (e.g. 7 chars), a branch/tag name like 'main', a value with whitespace, or running in a repo where localRevision() returns empty/malformed output.","commonSituations":"Copying a short SHA from `git log --oneline`, passing a tag name instead of a SHA, or running the script outside a git checkout so no revision can be resolved.","solutions":["Resolve the full SHA with `git rev-parse HEAD` (or `git rev-parse <ref>^{commit}`) and pass that as --expected-revision","Run the script from inside the git repository so localRevision() can produce a full SHA","Remove any surrounding whitespace or quotes from the value"],"exampleFix":"// before\n--expected-revision abc1234\n// after\n--expected-revision $(git rev-parse HEAD)","handlingStrategy":"validation","validationCode":"if (!/^[0-9a-f]{40}$/i.test(revision)) throw new Error('need full 40-char SHA');","typeGuard":"const isFullSha = (v) => typeof v === 'string' && /^[0-9a-f]{40}$/i.test(v);","tryCatchPattern":"try { run(args); } catch (e) { if (e.message.includes('expected revision')) { console.error('Pass $(git rev-parse HEAD)'); process.exitCode = 2; } else throw e; }","preventionTips":["Use `git rev-parse HEAD` — never short SHAs or ref names","Run the script inside the git checkout","Validate SHAs in CI before invoking the script"],"tags":["cli","git","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}