{"record":{"id":"82deb051becac09d","repo":"Hmbown/CodeWhale","slug":"cf-account-id-and-cf-api-token-are-required","errorCode":null,"errorMessage":"CF_ACCOUNT_ID and CF_API_TOKEN are required","messagePattern":"CF_ACCOUNT_ID and CF_API_TOKEN are required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"telemetry-ingest/scripts/report-active-installs.mjs","lineNumber":220,"sourceCode":"    headers: {\n      Authorization: `Bearer ${apiToken}`,\n      \"content-type\": \"text/plain; charset=utf-8\",\n    },\n    body: sql,\n  });\n  if (!response.ok) {\n    const body = (await response.text()).slice(0, 500);\n    throw new Error(`Cloudflare SQL request failed (${response.status}): ${body}`);\n  }\n  return response.json();\n}\n\nexport async function main(argv = process.argv.slice(2), env = process.env) {\n  const { days, json } = parseArgs(argv);\n  const accountId = env.CF_ACCOUNT_ID?.trim();\n  const apiToken = env.CF_API_TOKEN?.trim();\n  if (!accountId || !apiToken) {\n    throw new Error(\"CF_ACCOUNT_ID and CF_API_TOKEN are required\");\n  }\n  const rows = rowsFromResponse(\n    await querySql({ accountId, apiToken, sql: activeInstallsSql(days) }),\n  );\n  const newestEvent = newestEventFromResponse(\n    await querySql({ accountId, apiToken, sql: freshnessSql() }),\n  );\n  const now = new Date();\n  if (json) {\n    process.stdout.write(\n      `${JSON.stringify(\n        {\n          metric: \"observed_active_installs\",\n          timezone: \"UTC\",\n          days,\n          rows,\n          trend: trendSummary(rows, days, now),\n          freshness: {","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/telemetry-ingest/scripts/report-active-installs.mjs#L202-L238","documentation":"main() requires CF_ACCOUNT_ID and CF_API_TOKEN (both trimmed) from the environment and throws this message before any request when either is missing or blank. These are the script's only credential source; parseArgs has already succeeded by this point.","triggerScenarios":"Running report-active-installs.mjs in a shell or CI step without exporting the two variables, or with whitespace-only values.","commonSituations":"Local run without sourcing the secrets file; CI job missing the injected secrets; a token deleted during rotation but the env file not updated.","solutions":["Export both values obtained from the Cloudflare dashboard (account id) and a token with SQL read scope","In CI, add them as masked secrets wired into the step's env","Verify with `printenv CF_ACCOUNT_ID CF_API_TOKEN` before rerunning"],"exampleFix":"# before\nnode scripts/report-active-installs.mjs --json\n# after\nCF_ACCOUNT_ID=abc CF_API_TOKEN=xyz node scripts/report-active-installs.mjs --json","handlingStrategy":"validation","validationCode":"for (const name of ['CF_ACCOUNT_ID', 'CF_API_TOKEN']) {\n  if (!process.env[name]?.trim()) {\n    console.error(`${name} is required`);\n    process.exit(2);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await main(argv, env);\n} catch (error) {\n  if (/CF_ACCOUNT_ID and CF_API_TOKEN are required/.test(error.message)) { process.exit(2); }\n  throw error;\n}","preventionTips":["Wire the two names as CI masked secrets and fail the job early when absent","Keep a checked-in example env file listing required telemetry credentials"],"tags":["environment","secrets","config","telemetry"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}