{"record":{"id":"4229882375a4acbe","repo":"Zie619/n8n-workflows","slug":"invalid-row-structure","errorCode":null,"errorMessage":"Invalid row structure","messagePattern":"Invalid row structure","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workflows/Googleanalytics/0475_Googleanalytics_Code_Automate_Scheduled.json","lineNumber":527,"sourceCode":"      \"credentials\": {\n        \"googleAnalyticsOAuth2\": {\n          \"id\": \"8OdVzOGJqhJ3ti8k\",\n          \"name\": \"KBB Google Analytics account\"\n        }\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This googleAnalytics node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"15f3edcb-2e31-4faa-8db2-62da69bbfe8d\",\n      \"name\": \"Parse - Get Page Engagement This Week\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        1040,\n        740\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\": \"46cd21cd-c7f4-45cb-a724-db8a122f9de3\",\n      \"name\": \"Parse - Get Page Engagement Prior Week\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        1440,\n        740\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    // 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\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Googleanalytics/0475_Googleanalytics_Code_Automate_Scheduled.json#L509-L545","documentation":"The row-level guard in 'Parse - Get Page Engagement This Week': while mapping analyticsData.rows, a row is rejected when row.dimensionValues[0].value is falsy or row.metricValues is empty/missing. GA4 can return rows whose dimension value is the reserved string '(not set)' (truthy, passes) but also rows with undefined dimension values when a dimension is requested but unavailable.","triggerScenarios":"A GA4 report row where the first dimension (page path) is missing/undefined — e.g. querying ga:pageTitle or a custom dimension that has no value for some rows — or metricValues omitted for a row. One bad row kills the whole map() and the run.","commonSituations":"Report includes (other)/(not set) groupings; querying a custom dimension not populated on all events; GA4 data-model change adding rows with sparse values.","solutions":["Check the logged 'Invalid row structure:' line to see the offending row shape.","Filter before mapping (like the sibling prior-week node already does): rows.filter(r => r.dimensionValues?.[0]?.value && r.metricValues?.length).map(...).","Default missing dimensions to '(not set)' if those rows must be kept.","Use optional chaining on metricValues[1..3] (metricValues[1]?.value) so fewer-metric rows don't throw either."],"exampleFix":"// before\nconst simplified = analyticsData.rows.map(row => {\n    if (!row.dimensionValues?.[0]?.value || !row.metricValues?.length) {\n        throw new Error('Invalid row structure');\n    }\n    return { page: row.dimensionValues[0].value, /* ... */ };\n});\n\n// after (skip bad rows, as the prior-week node does)\nconst simplified = analyticsData.rows\n    .filter(row => row.dimensionValues?.[0]?.value && row.metricValues?.length)\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    }));","handlingStrategy":"type-guard","validationCode":"const validRows = analyticsData.rows.filter(r => r?.dimensionValues?.[0]?.value && r?.metricValues?.length);","typeGuard":"function isValidGa4Row(row) {\n  return Boolean(row?.dimensionValues?.[0]?.value && Array.isArray(row?.metricValues) && row.metricValues.length > 0);\n}","tryCatchPattern":null,"preventionTips":["Filter rows before mapping (as the prior-week node does) instead of throwing inside map().","Use optional chaining on every metric index (metricValues[1]?.value).","Map (not set)/(other) dimension values explicitly if they must be retained.","Log skipped-row counts so silent data loss is visible."],"tags":["n8n","google-analytics","ga4","row-validation","code-node"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}