{"record":{"id":"51a376d8107267dc","repo":"Hmbown/CodeWhale","slug":"days-must-be-an-integer-from-1-through-90","errorCode":null,"errorMessage":"--days must be an integer from 1 through 90","messagePattern":"--days must be an integer from 1 through 90","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"telemetry-ingest/scripts/report-active-installs.mjs","lineNumber":50,"sourceCode":"  // window that fills both sides of the 7-day trend.\n  let days = 15;\n  let json = false;\n  for (let index = 0; index < argv.length; index += 1) {\n    const arg = argv[index];\n    if (arg === \"--json\") {\n      json = true;\n      continue;\n    }\n    if (arg === \"--days\") {\n      const raw = argv[index + 1];\n      index += 1;\n      days = Number(raw);\n      continue;\n    }\n    throw new Error(`unknown argument: ${arg}`);\n  }\n  if (!Number.isInteger(days) || days < 1 || days > 90) {\n    throw new Error(\"--days must be an integer from 1 through 90\");\n  }\n  return { days, json };\n}\n\nexport function activeInstallsSql(days) {\n  return `SELECT\n  toDate(timestamp) AS day,\n  count(DISTINCT index1) AS active_installs,\n  sum(_sample_interval) AS sessions_started\nFROM codewhale_telemetry\nWHERE timestamp >= toStartOfDay(NOW()) - INTERVAL '${days - 1}' DAY\n  AND blob1 = 'session_start'\nGROUP BY day\nORDER BY day DESC\nFORMAT JSON`;\n}\n\n/** Newest ingested event of any kind — how stale the dataset is. */","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/telemetry-ingest/scripts/report-active-installs.mjs#L32-L68","documentation":"After parsing, `--days` is coerced with Number(raw); the guard requires Number.isInteger and a value from 1 through 90, else `--days must be an integer from 1 through 90`. A trailing `--days` with no value reads undefined → NaN → the same error. The bound exists because the value is interpolated into activeInstallsSql's `INTERVAL '${days - 1}' DAY` clause.","triggerScenarios":"`--days abc`, `--days 0`, `--days 91`, `--days 30.5`, or `--days` as the last argument with the value missing.","commonSituations":"Asking for a quarter (91+) or a year of data; passing a float; forgetting the value in a CI script line.","solutions":["Pass an integer in range, e.g. `--days 30`","If the flag is last on the line, remember it consumes the next token — supply the value","For ranges beyond 90 days, query the Cloudflare SQL API directly rather than widening the guard"],"exampleFix":"# before\nnode scripts/report-active-installs.mjs --days 365\n# after\nnode scripts/report-active-installs.mjs --days 90","handlingStrategy":"validation","validationCode":"const rawIndex = argv.indexOf('--days');\nconst days = Number(argv[rawIndex + 1]);\nif (!Number.isInteger(days) || days < 1 || days > 90) {\n  console.error('--days must be an integer from 1 through 90');\n  process.exit(2);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { days } = parseArgs(argv);\n} catch (error) {\n  if (/--days/.test(error.message)) { console.error('Pass e.g. --days 30'); process.exit(2); }\n  throw error;\n}","preventionTips":["Always supply an explicit integer value immediately after --days","For ranges beyond 90 days, query Cloudflare SQL directly instead of stretching the flag"],"tags":["cli","validation","arguments","telemetry"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}