Zie619/n8n-workflows · error · Error
Invalid data structure
Error message
Invalid data structure
What it means
Thrown by the 'Parse Umami data' Code node in a scheduled Umami-analytics workflow. The node's try block builds a simplified metrics object by reading data.pageviews.value, data.visitors.value, etc. If any of those nested objects is undefined, JavaScript raises a TypeError ('Cannot read properties of undefined'), which the catch swallows and rethrows as the generic 'Invalid data structure'.
Source
Thrown at workflows/Code/1671_Code_Schedule_Automation_Webhook.json:191
"parameters": {
"color": 4,
"width": 393.16558441558414,
"height": 504.17207792207796,
"content": "## Save analysis to baserow\n\nYou need to create a table in advance to save. \n- Date (date)\n- Summary (Long text)\n- Top pages (Long text)\n- Blog name (Long text)"
},
"typeVersion": 1,
"notes": "This stickyNote node performs automated tasks as part of the workflow."
},
{
"id": "f64cdfbd-712f-461c-b025-25f37e2bded8",
"name": "Parse Umami data",
"type": "n8n-nodes-base.code",
"position": [
940,
260
],
"parameters": {
"jsCode": "function transformToUrlString(items) {\n // In n8n, we need to check if items is an array and get the json property\n const data = items[0].json;\n \n if (!data) {\n console.log('No valid data found');\n return encodeURIComponent(JSON.stringify([]));\n }\n \n try {\n // Create a simplified object with the metrics\n const simplified = {\n pageviews: {\n value: parseInt(data.pageviews.value) || 0,\n prev: parseInt(data.pageviews.prev) || 0\n },\n visitors: {\n value: parseInt(data.visitors.value) || 0,\n prev: parseInt(data.visitors.prev) || 0\n },\n visits: {\n value: parseInt(data.visits.value) || 0,\n prev: parseInt(data.visits.prev) || 0\n },\n bounces: {\n value: parseInt(data.bounces.value) || 0,\n prev: parseInt(data.bounces.prev) || 0\n },\n totaltime: {\n value: parseInt(data.totaltime.value) || 0,\n prev: parseInt(data.totaltime.prev) || 0\n }\n };\n \n return encodeURIComponent(JSON.stringify(simplified));\n } catch (error) {\n console.log('Error processing data:', error);\n throw new Error('Invalid data structure');\n }\n}\n\n// Get the input data\nconst items = $input.all();\n\n// Process the data\nconst result = transformToUrlString(items);\n\n// Return the result\nreturn { json: { urlString: result } };"
},
"typeVersion": 2,
"notes": "This code node performs automated tasks as part of the workflow."
},
{
"id": "470715b6-0878-48b8-b6c6-40de27fbc966",
"name": "Send data to A.I.",
"type": "n8n-nodes-base.httpRequest",
"position": [
1140,
260
],
"parameters": {
"url": "{{ $env.API_BASE_URL }}",
"method": "POST",
"options": {},
"jsonBody": "={\n \"model\": \"meta-llama/llama-3.1-70b-instruct:free\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"You are an SEO expert. Here is data from Umami analytics of Pennibnotes.com. Where X is URL and Y is number of visitors. Give me a table summary of this data in markdown format:{{ $('Parse Umami data').item.json.urlString }}.\"\n }\n ]\n}",
"sendBody": true,View on GitHub (pinned to 94007c1445)
Solutions
- Inspect the console log line 'Error processing data:' in the execution — it prints the original TypeError telling you exactly which field (e.g. pageviews) is undefined.
- Check the upstream HTTP Request node response for the actual Umami payload (auth token valid? correct website id? date range has data?).
- Harden the reader with optional chaining and defaults so missing metrics become 0 instead of an exception, if partial data is acceptable.
- Pin/verify the Umami API version the workflow was built against after any self-hosted upgrade.
Example fix
// before
const simplified = {
pageviews: {
value: parseInt(data.pageviews.value) || 0,
prev: parseInt(data.pageviews.prev) || 0
},
// ... same for visitors, visits, bounces, totaltime
};
// after
const pick = (obj, k) => ({ value: parseInt(obj?.[k]?.value) || 0, prev: parseInt(obj?.[k]?.prev) || 0 });
const simplified = {
pageviews: pick(data, 'pageviews'),
visitors: pick(data, 'visitors'),
visits: pick(data, 'visits'),
bounces: pick(data, 'bounces'),
totaltime: pick(data, 'totaltime')
}; Defensive patterns
Strategy: type-guard
Validate before calling
const data = items[0]?.json; const REQUIRED = ['pageviews', 'visitors', 'visits', 'bounces', 'totaltime']; const complete = data && REQUIRED.every(k => data[k] && 'value' in data[k]);
Type guard
function isUmamiStats(d) {
return Boolean(d && ['pageviews','visitors','visits','bounces','totaltime']
.every(k => d[k] && typeof d[k].value !== 'undefined'));
} Try / catch
try { simplified = buildMetrics(data); } catch (e) { throw new Error(`Umami payload unexpected shape (${e.message}): ${JSON.stringify(data).slice(0, 200)}`); } Prevention
- Use optional chaining (data?.pageviews?.value) for every nested read.
- Check the upstream HTTP node's status/response before parsing.
- Re-throw with the original TypeError message appended — never swallow it into a generic string.
- Pin the Umami API version and re-test parsing after upgrades.
When it happens
Trigger: The Umami stats API response shape changed or returned an error body (auth failure, invalid range, empty result) so items[0].json exists (truthy) but lacks pageviews/visitors/visits/bounces/totaltime sub-objects. Any one missing sub-object triggers the TypeError → catch → this error.
Common situations: Umami self-hosted version upgrade changing the /api/websites/:id/stats response; expired or missing auth token making the HTTP node return an error JSON that still lands in json; querying a date range with no data where Umami omits keys; API rate limiting returning a non-metrics body.
Related errors
- Invalid data structure
- Approved quantity must be greater than 0
- ${fileName} → ${baseName} → Unrecognized file name structure
- The video ID parameter is empty.
- Could not find video URL in the JSON data.
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/b90d52b6678d1da3.
Report an issue: GitHub.