{"record":{"id":"f9e8f78949c87013","repo":"Zie619/n8n-workflows","slug":"invalid-data-structure-f9e8f7","errorCode":null,"errorMessage":"Invalid data structure","messagePattern":"Invalid data structure","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workflows/Googleanalytics/1480_Googleanalytics_Code_Automation_Webhook.json","lineNumber":204,"sourceCode":"              \"listName\": \"other\"\n            }\n          ]\n        },\n        \"additionalFields\": {}\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This googleAnalytics node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"f8dac36b-9e8a-407f-b923-b4cea368f1bc\",\n      \"name\": \"Parse data from Google Analytics\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        880,\n        460\n      ],\n      \"parameters\": {\n        \"jsCode\": \"function transformToUrlString(items) {\\n // Debug logging\\n console.log('Input items:', JSON.stringify(items, null, 2));\\n \\n // Check if items is an array and has content\\n if (!Array.isArray(items) || items.length === 0) {\\n console.log('Items is not an array or is empty');\\n throw new Error('Invalid data structure');\\n }\\n\\n // Check if first item exists and has json property\\n if (!items[0] || !items[0].json) {\\n console.log('First item is missing or has no json property');\\n throw new Error('Invalid data structure');\\n }\\n\\n // Get the analytics data\\n const analyticsData = items[0].json;\\n \\n // Check if analyticsData has rows\\n if (!analyticsData || !Array.isArray(analyticsData.rows)) {\\n console.log('Analytics data is missing or has no rows array');\\n throw new Error('Invalid data structure');\\n }\\n \\n // Map each row to a simplified object\\n const simplified = analyticsData.rows.map(row => {\\n if (!row.dimensionValues?.[0]?.value || !row.metricValues?.length) {\\n console.log('Invalid row structure:', row);\\n throw new Error('Invalid row structure');\\n }\\n \\n return {\\n page: row.dimensionValues[0].value,\\n pageViews: parseInt(row.metricValues[0].value) || 0,\\n activeUsers: parseInt(row.metricValues[1].value) || 0,\\n viewsPerUser: parseFloat(row.metricValues[2].value) || 0,\\n eventCount: parseInt(row.metricValues[3].value) || 0\\n };\\n });\\n \\n // Convert to JSON string and encode for URL\\n return encodeURIComponent(JSON.stringify(simplified));\\n}\\n\\n// Get input data and transform it\\nconst urlString = transformToUrlString($input.all());\\n\\n// Return the result\\nreturn { json: { urlString } };\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"ed880442-c92e-4347-b277-e8794aea6fbc\",\n      \"name\": \"Parse GA data\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        1240,\n        460\n      ],\n      \"parameters\": {\n        \"jsCode\": \"function transformToUrlString(items) {\\n // Debug logging\\n console.log('Input items:', JSON.stringify(items, null, 2));\\n \\n // Check if items is an array and has content\\n if (!Array.isArray(items) || items.length === 0) {\\n console.log('Items is not an array or is empty');\\n throw new Error('Invalid data structure');\\n }\\n\\n // Check if first item exists and has json property\\n if (!items[0] || !items[0].json) {\\n console.log('First item is missing or has no json property');\\n throw new Error('Invalid data structure');\\n }\\n\\n // Get the analytics data\\n const analyticsData = items[0].json;\\n \\n // Check if analyticsData has rows\\n if (!analyticsData || !Array.isArray(analyticsData.rows)) {\\n console.log('Analytics data is missing or has no rows array');\\n throw new Error('Invalid data structure');\\n }\\n \\n // Map each row to a simplified object\\n const simplified = analyticsData.rows.map(row => {\\n if (!row.dimensionValues?.[0]?.value || !row.metricValues?.length) {\\n console.log('Invalid row structure:', row);\\n throw new Error('Invalid row structure');\\n }\\n \\n return {\\n page: row.dimensionValues[0].value,\\n pageViews: parseInt(row.metricValues[0].value) || 0,\\n activeUsers: parseInt(row.metricValues[1].value) || 0,\\n viewsPerUser: parseFloat(row.metricValues[2].value) || 0,\\n eventCount: parseInt(row.metricValues[3].value) || 0\\n };\\n });\\n \\n // Convert to JSON string and encode for URL\\n return encodeURIComponent(JSON.stringify(simplified));\\n}\\n\\n// Get input data and transform it\\nconst urlString = transformToUrlString($input.all());\\n\\n// Return the result\\nreturn { json: { urlString } };\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Googleanalytics/1480_Googleanalytics_Code_Automation_Webhook.json#L186-L222","documentation":"Literal Error thrown by the strict Code node \"Parse data from Google Analytics\" (1480_Googleanalytics_Code_Automation_Webhook.json:204). Unlike the 0475 variants, this parser THROWS (not returns empty) whenever the shape check fails: items not a non-empty array, items[0].json missing, or analyticsData.rows not an Array. The upstream node is a GA4 googleAnalytics node (typeVersion 2, propertyId 460520224, metrics screenPageViews/activeUsers/screenPageViewsPerUser/eventCount, dimension unifiedScreenName) — and the GA4 Data API omits the rows key entirely when a report matches zero rows, which is the dominant trigger.","triggerScenarios":"GA4 runReport for the date range ({{$today.minus({days: 14})}} to fixed endDate) returns zero rows → response has rowCount: 0 and NO rows property → Array.isArray(analyticsData.rows) is false → throw. Also: upstream GA node disabled/misrouted so the Code node receives no or different items, or webhook-triggered execution where the GA node output shape changed after an n8n upgrade.","commonSituations":"New GA4 property with no traffic in the window; endDate hardcoded to 2024-10-23 so every later execution queries a dead range and gets zero rows; GA credentials/property misconfigured so the upstream node errors and passes nothing; n8n major-version change to the GA node output envelope.","solutions":["Open the failed execution, expand this Code node, and read which console.log fired ('Items is not an array or is empty' vs 'missing or has no rows array') — it tells you whether input is empty or rows is missing.","Fix the stale hardcoded endDate (2024-10-23) in the upstream googleAnalytics node — use a dynamic {{$today}} expression so the range always contains data.","If zero rows is legitimate, return encodeURIComponent(JSON.stringify([])) instead of throwing, and let a downstream IF node handle the empty urlString.","Confirm the upstream GA4 node executes on the same branch and its propertyId still reports data for the range.","After any n8n upgrade, re-run once and pin the workflow to a tested n8n version if the GA node output shape changed."],"exampleFix":"// before\nif (!analyticsData || !Array.isArray(analyticsData.rows)) {\n  console.log('Analytics data is missing or has no rows array');\n  throw new Error('Invalid data structure');\n}\n\n// after\nif (!analyticsData || !Array.isArray(analyticsData.rows)) {\n  console.log('No rows in GA4 report (rowCount:', analyticsData?.rowCount, ') — returning empty payload');\n  return encodeURIComponent(JSON.stringify([]));\n}","handlingStrategy":"validation","validationCode":"// Inside the Code node, before parsing\nconst items = $input.all();\nconst report = items?.[0]?.json;\nif (!report || !Array.isArray(report.rows)) {\n  // GA4 omits `rows` on zero-row reports — this is data, not an error\n  const rowCount = report?.rowCount ?? 'unknown';\n  console.log('Zero-row or unexpected GA4 report; rowCount:', rowCount);\n  return { json: { urlString: encodeURIComponent(JSON.stringify([])), empty: true } };\n}","typeGuard":"const isGaReport = (x) => !!x && (Array.isArray(x.rows) || typeof x.rowCount === 'number');\nconst isGaRow = (r) => !!r?.dimensionValues?.[0]?.value && (r?.metricValues?.length ?? 0) >= 4;","tryCatchPattern":null,"preventionTips":["Never hardcode report endDates — use {{$today}} expressions so ranges never go stale.","Treat missing rows as an empty report and branch downstream on an explicit empty flag.","Keep the parser's metric count in sync with the GA node's metricsGA4 list (here: 4 metrics)."],"tags":["n8n","code-node","google-analytics","ga4","empty-results","validation"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}