{"record":{"id":"5ffc3ade2016f9eb","repo":"Hmbown/CodeWhale","slug":"attempts-must-be-an-integer-from-1-to-20","errorCode":null,"errorMessage":"--attempts must be an integer from 1 to 20","messagePattern":"--attempts must be an integer from 1 to 20","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/compare-deployed-facts.mjs","lineNumber":116,"sourceCode":"\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\" };\n  let differences = [];\n\n  for (let attempt = 1; attempt <= args.attempts; attempt += 1) {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/web/scripts/compare-deployed-facts.mjs#L98-L134","documentation":"Thrown by the post-deploy drift gate web/scripts/compare-deployed-facts.mjs when an explicitly passed --attempts value is not an integer in [1, 20]. The value goes through Number(argv[++index]), so a non-numeric token becomes NaN, decimals like 3.5 fail Number.isInteger, and 0 or 21 fail the range check. When the flag is omitted the script defaults to 6 attempts with --require-current and 1 otherwise, so only an explicitly bad value reaches this throw.","triggerScenarios":"Running the script with --attempts 0, --attempts 21, --attempts 3.5, --attempts abc (NaN), or a trailing --attempts whose value token is missing so Number(undefined) yields NaN.","commonSituations":"CI pipelines injecting retry counts from matrix variables or env vars that arrive as floats or empty strings; copying a --tries-style flag from another tool; wrapper scripts quoting away the value token.","solutions":["Pass an integer from 1 to 20, for example --attempts 6","Omit the flag entirely; the default is already 6 with --require-current and 1 without","Coerce and clamp sourced values before invoking: Math.min(20, Math.max(1, parseInt(value, 10)))","Check wrapper scripts for a --attempts token whose value was dropped by quoting"],"exampleFix":"// before\nnode web/scripts/compare-deployed-facts.mjs --require-current --attempts 3.5\n// after\nnode web/scripts/compare-deployed-facts.mjs --require-current --attempts 4","handlingStrategy":"validation","validationCode":"// validate before spawning the gate script\nconst raw = Number(process.env.DEPLOY_ATTEMPTS ?? '');\nconst valid = Number.isInteger(raw) && raw >= 1 && raw <= 20;\nconst args = ['web/scripts/compare-deployed-facts.mjs'];\nif (valid) args.push('--attempts', String(raw)); // otherwise let the script default apply","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Type retry counts as integers where they originate (CI variables, config schemas)","Prefer omitting optional numeric flags over forwarding computed values","Echo accepted ranges in wrapper usage text beside where flags are assembled"],"tags":["cli","validation","nodejs","deployment"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}