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

  1. Check the execution log for which console.log line fired ('no rows array' vs 'not an array or empty') to identify the exact failure.
  2. Test the upstream GoogleAnalytics node directly — verify property ID, credentials, and that the date range contains data.
  3. Treat a missing rows key as an empty report: const rows = analyticsData.rows ?? []; and skip downstream AI/report steps when empty.
  4. 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

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


AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15). Data as JSON: /api/errors/4e8cc2c6232a34fd. Report an issue: GitHub.