Zie619/n8n-workflows · error · Error
Invalid data structure
Error message
Invalid data structure
What it means
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.
Source
Thrown at workflows/Googleanalytics/1480_Googleanalytics_Code_Automation_Webhook.json:204
"listName": "other"
}
]
},
"additionalFields": {}
},
"typeVersion": 2,
"notes": "This googleAnalytics node performs automated tasks as part of the workflow."
},
{
"id": "f8dac36b-9e8a-407f-b923-b4cea368f1bc",
"name": "Parse data from Google Analytics",
"type": "n8n-nodes-base.code",
"position": [
880,
460
],
"parameters": {
"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 } };"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},
{
"id": "ed880442-c92e-4347-b277-e8794aea6fbc",
"name": "Parse GA data",
"type": "n8n-nodes-base.code",
"position": [
1240,
460
],
"parameters": {
"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 } };"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},View on GitHub (pinned to 94007c1445)
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.
Example fix
// before
if (!analyticsData || !Array.isArray(analyticsData.rows)) {
console.log('Analytics data is missing or has no rows array');
throw new Error('Invalid data structure');
}
// after
if (!analyticsData || !Array.isArray(analyticsData.rows)) {
console.log('No rows in GA4 report (rowCount:', analyticsData?.rowCount, ') — returning empty payload');
return encodeURIComponent(JSON.stringify([]));
} Defensive patterns
Strategy: validation
Validate before calling
// Inside the Code node, before parsing
const items = $input.all();
const report = items?.[0]?.json;
if (!report || !Array.isArray(report.rows)) {
// GA4 omits `rows` on zero-row reports — this is data, not an error
const rowCount = report?.rowCount ?? 'unknown';
console.log('Zero-row or unexpected GA4 report; rowCount:', rowCount);
return { json: { urlString: encodeURIComponent(JSON.stringify([])), empty: true } };
} Type guard
const isGaReport = (x) => !!x && (Array.isArray(x.rows) || typeof x.rowCount === 'number'); const isGaRow = (r) => !!r?.dimensionValues?.[0]?.value && (r?.metricValues?.length ?? 0) >= 4;
Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid data structure
- Invalid data structure
- Invalid row structure
- Invalid row structure
- Invalid data structure
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/f9e8f78949c87013.
Report an issue: GitHub.