{"record":{"id":"580411e963cb2a0e","repo":"Hmbown/CodeWhale","slug":"retry-delay-ms-must-be-between-0-and-30000","errorCode":null,"errorMessage":"--retry-delay-ms must be between 0 and 30000","messagePattern":"--retry-delay-ms must be between 0 and 30000","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/compare-deployed-facts.mjs","lineNumber":119,"sourceCode":"  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) {\n    last = await fetchReceipt(endpoint);\n    differences = last.receipt ? compare(expected, last.receipt) : [];\n    if (last.receipt && differences.length === 0) break;","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/web/scripts/compare-deployed-facts.mjs#L101-L137","documentation":"The same gate script rejects --retry-delay-ms values that are not finite numbers in [0, 30000]. The check uses Number.isFinite, so NaN (non-numeric token) and Infinity fail, while fractional values such as 500.5 are accepted; the default is 3000 ms. The 30-second cap bounds how long a single retry cycle can stall a deploy pipeline.","triggerScenarios":"Invoking the script with --retry-delay-ms -1, --retry-delay-ms 31000, --retry-delay-ms abc, --retry-delay-ms Infinity, or a missing value token after the flag.","commonSituations":"Configs written in seconds copied verbatim (60000 for a 60-second wait); env interpolation producing an empty string; trying to stretch total wait time via delay instead of attempts.","solutions":["Keep the value in [0, 30000] milliseconds; the default 3000 is usually right","Extend total patience with --attempts (up to 20), not with a longer delay","Convert and clamp second-based settings before invoking: Math.min(30000, seconds * 1000)","Omit the flag to accept the 3000 ms default"],"exampleFix":"// before\nnode web/scripts/compare-deployed-facts.mjs --attempts 8 --retry-delay-ms 60000\n// after\nnode web/scripts/compare-deployed-facts.mjs --attempts 12 --retry-delay-ms 30000","handlingStrategy":"validation","validationCode":"const ms = Number(settings.retryDelaySeconds ?? 3) * 1000;\nconst safeDelay = Number.isFinite(ms) ? Math.min(30_000, Math.max(0, ms)) : 3_000;\nrun('--retry-delay-ms', String(safeDelay));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store delays in one unit and convert at the boundary","Cap per-cycle delays at 30s and extend patience with --attempts instead","Validate sourced numbers before building command lines"],"tags":["cli","validation","nodejs","retry","deployment"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}