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

  1. 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.
  2. Fix the stale hardcoded endDate (2024-10-23) in the upstream googleAnalytics node — use a dynamic {{$today}} expression so the range always contains data.
  3. If zero rows is legitimate, return encodeURIComponent(JSON.stringify([])) instead of throwing, and let a downstream IF node handle the empty urlString.
  4. Confirm the upstream GA4 node executes on the same branch and its propertyId still reports data for the range.
  5. 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

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


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