{"record":{"id":"a534dd385727779c","repo":"Hmbown/CodeWhale","slug":"cloudflare-sql-response-did-not-contain-a-data-array","errorCode":null,"errorMessage":"Cloudflare SQL response did not contain a data array","messagePattern":"Cloudflare SQL response did not contain a data array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"telemetry-ingest/scripts/report-active-installs.mjs","lineNumber":79,"sourceCode":"WHERE 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. */\nexport function freshnessSql() {\n  return `SELECT\n  max(timestamp) AS newest_event\nFROM codewhale_telemetry\nFORMAT JSON`;\n}\n\nfunction dataRows(payload) {\n  const rows = Array.isArray(payload) ? payload : payload?.data;\n  if (!Array.isArray(rows)) {\n    throw new Error(\"Cloudflare SQL response did not contain a data array\");\n  }\n  return rows;\n}\n\nexport function rowsFromResponse(payload) {\n  return dataRows(payload).map((row) => ({\n    day: String(row.day),\n    active_installs: Number(row.active_installs),\n    sessions_started: Number(row.sessions_started),\n  }));\n}\n\n/** `null` when the dataset has no rows at all. */\nexport function newestEventFromResponse(payload) {\n  const raw = dataRows(payload)[0]?.newest_event;\n  if (raw === undefined || raw === null || String(raw).startsWith(\"0000\")) {\n    return null;\n  }","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/telemetry-ingest/scripts/report-active-installs.mjs#L61-L97","documentation":"dataRows() normalizes a Cloudflare D1 SQL API response to an array of row objects: it accepts either a raw array or an object with a `data` array. If neither shape holds an array, it throws, because every downstream row-mapping would crash on a non-array.","triggerScenarios":"Calling rowsFromResponse()/dataRows() with a payload that is neither an array nor an object whose `data` field is an array — e.g. an unexpected Cloudflare API envelope, an error object returned as JSON, or null/undefined from a failed fetch.","commonSituations":"The D1 API changed its response envelope (e.g. results nested under `result.results` instead of `data`); a proxy or auth failure returned a JSON error body; the script was pointed at a mock/stub with a different shape.","solutions":["Log the full payload to inspect the actual response shape (JSON.stringify(payload, null, 2)).","If the Cloudflare envelope is `{results: [...]}` or `{result: {results: [...]}}`, update dataRows to read the correct field.","Check that the request succeeded (response.ok) and authentication is valid before parsing; an error body often lacks `data`.","If callers may pass an already-array payload, ensure they are not double-wrapping or passing `{data: {…}}` (non-array data)."],"exampleFix":"// before\nconst rows = Array.isArray(payload) ? payload : payload?.data;\n// after\nconst rows = Array.isArray(payload) ? payload : payload?.data ?? payload?.result?.results;","handlingStrategy":"type-guard","validationCode":"if (!Array.isArray(payload) && !Array.isArray(payload?.data)) console.error(\"unexpected D1 payload:\", JSON.stringify(payload));","typeGuard":"function hasRows(p) { return Array.isArray(p) || Array.isArray(p?.data); }","tryCatchPattern":"try { const rows = rowsFromResponse(res); } catch (e) { if (e.message.includes(\"data array\")) { console.error(JSON.stringify(res)); } throw e; }","preventionTips":["Check response.ok before parsing JSON.","Inspect the Cloudflare D1 API envelope for your API version before relying on `data`.","Add a debug dump of the raw payload when the shape guard fails.","Pin/monitor Cloudflare API version changes."],"tags":["cloudflare","d1","response-shape","http"],"backgroundTag":"unexpected-response-shape","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"}