Zie619/n8n-workflows · error · Error
Invalid data structure
Error message
Invalid data structure
What it means
Thrown by 'Parse - Get Page Engagement This Week' in a Google Analytics 4 reporting workflow. The node validates the upstream GoogleAnalytics node output: items must be a non-empty array, items[0].json must exist, and analyticsData.rows must be an array. 'Invalid data structure' covers all three failures; the console.log lines in the execution pinpoint which check tripped.
Source
Thrown at workflows/Googleanalytics/0475_Googleanalytics_Code_Automate_Scheduled.json:527
"credentials": {
"googleAnalyticsOAuth2": {
"id": "8OdVzOGJqhJ3ti8k",
"name": "KBB Google Analytics account"
}
},
"typeVersion": 2,
"notes": "This googleAnalytics node performs automated tasks as part of the workflow."
},
{
"id": "15f3edcb-2e31-4faa-8db2-62da69bbfe8d",
"name": "Parse - Get Page Engagement This Week",
"type": "n8n-nodes-base.code",
"position": [
1040,
740
],
"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": "46cd21cd-c7f4-45cb-a724-db8a122f9de3",
"name": "Parse - Get Page Engagement Prior Week",
"type": "n8n-nodes-base.code",
"position": [
1440,
740
],
"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 // Filter out invalid rows and map each valid row to a simplified object\n const simplified = analyticsData.rows\n .filter(row => {\n // Check if row is valid and its properties exist\n const isValid = row \n && row.dimensionValues \n && row.dimensionValues[0] \n && row.dimensionValues[0].value \n && row.metricValues \n && row.metricValues.length > 0;\n \n if (!isValid) {\n console.log('Ignoring invalid or null row:', row);\n }\n return isValid;\n })\n .map(row => ({\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 // 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"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},View on GitHub (pinned to 94007c1445)
Solutions
- Check the execution log for which console.log line fired ('no rows array' vs 'not an array or empty') to identify the exact failure.
- Test the upstream GoogleAnalytics node directly — verify property ID, credentials, and that the date range contains data.
- Treat a missing rows key as an empty report: const rows = analyticsData.rows ?? []; and skip downstream AI/report steps when empty.
- For genuinely empty weeks, add an IF branch that exits gracefully instead of throwing.
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 (GA4 omits rows when the report is empty)
if (!analyticsData || typeof analyticsData !== 'object') {
throw new Error(`Invalid data structure: ${JSON.stringify(analyticsData).slice(0, 200)}`);
}
const rows = Array.isArray(analyticsData.rows) ? analyticsData.rows : []; // empty week, not an error Defensive patterns
Strategy: validation
Validate before calling
const j = items[0]?.json; const ok = Boolean(j) && (Array.isArray(j.rows) || j.rows === undefined); // GA4 omits rows when empty const rows = j?.rows ?? [];
Type guard
function isGa4Report(j) {
return Boolean(j && typeof j === 'object' && !j.error && (Array.isArray(j.rows) || !('rows' in j)));
} Prevention
- Treat a missing rows key as an empty report — GA4 omits it, it is not an error.
- Branch on empty data (IF node) and skip the AI/report step instead of throwing.
- Check the analytics node for error objects (j.error / j.code) and fail with their message.
- Log which guard tripped (the node already does) and read it before changing code.
When it happens
Trigger: The GA4 runReport response has no rows key (GA4 omits rows entirely when the report is empty — a week with zero traffic), the GoogleAnalytics node returned an error object instead of a report (bad property ID, expired credentials), or items came back empty.
Common situations: New/low-traffic GA4 property with no events in the date range (rows is undefined, not an empty array — this is the classic GA4 gotcha); service account credentials expired or property ID misconfigured; date-range expressions in the trigger producing empty windows.
Related errors
- Invalid row structure
- Invalid data structure
- Invalid row structure
- Invalid data structure
- Invalid data structure
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/4e8cc2c6232a34fd.
Report an issue: GitHub.