{"record":{"id":"98fa01b0d1512780","repo":"Zie619/n8n-workflows","slug":"invalid-data-structure-98fa01","errorCode":null,"errorMessage":"Invalid data structure","messagePattern":"Invalid data structure","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workflows/Googleanalytics/1652_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/1652_Googleanalytics_Code_Automation_Webhook.json#L186-L222","documentation":"Thrown by the strict 'Parse data from Google Analytics' Code node (workflow 1652). Unlike the loose variant, this code validates explicitly and throws 'Invalid data structure' when: items is not a non-empty array, items[0].json is missing, or items[0].json has no Array.isArray(rows). It never falls back to an empty result — any deviation aborts the branch.","triggerScenarios":"The upstream googleAnalytics node returns zero items (report ran against a date range with no data), returns items whose json is empty (alwaysOutputData on an empty result), or returns the report wrapped differently than {rows: [...]}. A row-level mismatch throws the distinct 'Invalid row structure' message instead, so this error is specifically about the top-level container.","commonSituations":"Weekly report cron hitting a brand-new GA4 property with no events yet; date-range expression resolving to a window with zero sessions; upstream GA node erroring but alwaysOutputData emitting one empty item; template workflow pointed at a property the credentials cannot read.","solutions":["Run the upstream googleAnalytics node manually and inspect its output: confirm items exist and json.rows is present for the configured date range.","If empty results are legitimate, return the encoded empty array instead of throwing (see exampleFix).","Fix the date-range/property configuration on the GA node if the report should have data.","Turn off alwaysOutputData on the GA node so empty results are visibly empty rather than a misleading empty item."],"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  // No data for this period is a valid outcome; downstream A.I. step handles []\n  return encodeURIComponent(JSON.stringify([]));\n}","handlingStrategy":"validation","validationCode":"const items = $input.all();\nconst hasReport = items.length > 0 && Array.isArray(items[0]?.json?.rows);\nif (!hasReport) {\n  // decide: empty period -> return encodeURIComponent(JSON.stringify([]))\n}","typeGuard":"function isGaReportItem(item) {\n  return !!item?.json && Array.isArray(item.json.rows);\n}","tryCatchPattern":"try { /* map rows */ } catch (e) { throw new Error(`GA parse failed for item: ${e.message}`); }","preventionTips":["Decide up front whether 'no rows' is an error or a valid empty result, and encode that decision instead of a blanket throw.","Disable alwaysOutputData on GA nodes so empty results are visibly empty.","Log rowCount when returning early so silent empty periods are traceable."],"tags":["n8n","google-analytics","ga4","empty-results","strict-validation"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}