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

  1. 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.
  2. Check the upstream HTTP Request node response for the actual Umami payload (auth token valid? correct website id? date range has data?).
  3. Harden the reader with optional chaining and defaults so missing metrics become 0 instead of an exception, if partial data is acceptable.
  4. 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

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


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