Zie619/n8n-workflows · warning · Error

Failed to convert start time. Reason: ${startDtTargetZone.in

Error message

Failed to convert start time. Reason: ${startDtTargetZone.invalidReason || 'Unknown'}

What it means

Produced in 'Convert time to CST America / Chicago' when Luxon's DateTime.fromISO(starttime, {zone:'utc'}).setZone(timeZone) yields an invalid DateTime (isValid false, e.g. invalidReason 'bad input format' or 'unsupported zone'). Important: this throw is INSIDE the node's try/catch, which catches it and writes item.json.conversionError = error.message — so it does not fail the workflow; the message surfaces as a per-item error flag consumed downstream.

Source

Thrown at workflows/Webhook/0829_Webhook_Code_Create_Webhook.json:1251

              "type": "string",
              "value": "={{ $json.body.message.call.customer.number }}"
            }
          ]
        }
      },
      "typeVersion": 3.3,
      "notes": "This set node performs automated tasks as part of the workflow."
    },
    {
      "id": "b4bc5cee-d631-4aa8-a3ff-59a0b647d36a",
      "name": "Convert time to CST America / Chicago",
      "type": "n8n-nodes-base.code",
      "position": [
        1480,
        1580
      ],
      "parameters": {
        "jsCode": "// Get all input items\nconst items = $input.all();\n\n// Loop through each item\nfor (const item of items) {\n  // Get the values from the current item's JSON data\n  const startTimeUTC = item.json.starttime;\n  const endTimeUTC = item.json.endtime;\n  const targetTimeZone = item.json.timeZone; // e.g., \"America/Chicago\"\n\n  // Basic validation: ensure the necessary fields exist\n  if (!startTimeUTC || !endTimeUTC || !targetTimeZone) {\n    console.warn(`Skipping item due to missing time data or timezone. Item JSON: ${JSON.stringify(item.json)}`);\n    item.json.conversionError = \"Missing starttime, endtime, or timeZone\";\n    continue; // Move to the next item\n  }\n\n  try {\n    // --- Start Time Conversion ---\n    // Parse the original UTC ISO string using Luxon (NO $ prefix)\n    const startDt = luxon.DateTime.fromISO(startTimeUTC, { zone: 'utc' });\n\n    // Convert the DateTime object to the target timezone\n    const startDtTargetZone = startDt.setZone(targetTimeZone);\n\n    // Check if the conversion was valid\n    if (!startDtTargetZone.isValid) {\n      throw new Error(`Failed to convert start time. Reason: ${startDtTargetZone.invalidReason || 'Unknown'}`);\n    }\n\n    // Format the result back into an ISO string with the correct offset\n    item.json.starttime = startDtTargetZone.toISO();\n\n    // --- End Time Conversion ---\n    // Parse the original UTC ISO string using Luxon (NO $ prefix)\n    const endDt = luxon.DateTime.fromISO(endTimeUTC, { zone: 'utc' });\n\n    // Convert the DateTime object to the target timezone\n    const endDtTargetZone = endDt.setZone(targetTimeZone);\n\n     // Check if the conversion was valid\n    if (!endDtTargetZone.isValid) {\n      throw new Error(`Failed to convert end time. Reason: ${endDtTargetZone.invalidReason || 'Unknown'}`);\n    }\n\n    // Format the result back into an ISO string with the correct offset\n    item.json.endtime = endDtTargetZone.toISO();\n\n    // Optionally remove the error flag if conversion was successful this time\n    delete item.json.conversionError;\n\n  } catch (error) {\n    console.error(`Error converting time for item: ${JSON.stringify(item.json)}. Error: ${error.message}`);\n    // Add/update the error flag to the item's JSON\n    item.json.conversionError = error.message;\n  }\n}\n// Return the modified array of items\nreturn items;"
      },
      "typeVersion": 2,
      "notes": "This code node performs automated tasks as part of the workflow."
    },
    {
      "id": "2c8b2884-14d3-4bd4-92d8-6e402ca3a8de",
      "name": "Create Event",
      "type": "n8n-nodes-base.googleCalendar",
      "onError": "continueErrorOutput",
      "position": [
        1700,
        1580
      ],
      "parameters": {
        "end": "={{ $json.endtime }}",
        "start": "={{ $json.starttime }}",
        "calendar": {
          "__rl": true,

View on GitHub (pinned to 94007c1445)

Solutions

  1. Log conversionError items: their message names the Luxon invalidReason; fix the source to emit full ISO 8601 UTC strings (e.g., 2024-03-01T14:00:00Z).
  2. Normalize before parsing: if starttime is a number, treat it as epoch ms (DateTime.fromMillis); if it lacks zone info, append 'Z' or use {zone:'utc'} deliberately.
  3. Validate timeZone against a known IANA list or intercept common aliases (CST->America/Chicago) before setZone.
  4. Add a Filter/IF after this node to route conversionError items to a review path instead of letting them reach Google Calendar.

Example fix

// before
const startDt = luxon.DateTime.fromISO(startTimeUTC, { zone: 'utc' });
const startDtTargetZone = startDt.setZone(targetTimeZone);
if (!startDtTargetZone.isValid) {
  throw new Error(`Failed to convert start time. Reason: ${startDtTargetZone.invalidReason || 'Unknown'}`);
}

// after - normalize common inputs first
const zoneAliases = { CST: 'America/Chicago', CDT: 'America/Chicago', EST: 'America/New York' };
const tz = zoneAliases[targetTimeZone] || targetTimeZone.trim();
let startDt = luxon.DateTime.fromISO(String(startTimeUTC), { zone: 'utc' });
if (!startDt.isValid && typeof startTimeUTC === 'number') {
  startDt = luxon.DateTime.fromMillis(startTimeUTC);
}
const startDtTargetZone = startDt.setZone(tz);
if (!startDtTargetZone.isValid) {
  item.json.conversionError = `Failed to convert start time (${startTimeUTC}) to ${tz}: ${startDtTargetZone.invalidReason}`;
  continue;
}
Defensive patterns

Strategy: fallback

Validate before calling

const isValidIso = (s) => typeof s === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})$/.test(s);
const zoneAliases = { CST: 'America/Chicago', CDT: 'America/Chicago' };
const tz = zoneAliases[targetTimeZone] || String(targetTimeZone || '').trim();
if (!isValidIso(startTimeUTC) || !isValidIso(endTimeUTC)) {
  item.json.conversionError = `Non-ISO datetime: start=${startTimeUTC}, end=${endTimeUTC}`;
  continue;
}

Type guard

const isIanaZone = (z) =>
  typeof z === 'string' && /^(Africa|America|Antarctica|Asia|Atlantic|Australia|Europe|Indian|Pacific|Etc)\//.test(z) || z === 'UTC';

Try / catch

try {
  item.json.starttime = luxon.DateTime.fromISO(startTimeUTC, { zone: 'utc' }).setZone(tz).toISO();
  item.json.endtime = luxon.DateTime.fromISO(endTimeUTC, { zone: 'utc' }).setZone(tz).toISO();
  delete item.json.conversionError;
} catch (e) {
  item.json.conversionError = e.message; // per-item flag, workflow continues
}

Prevention

When it happens

Trigger: starttime/endtime not a valid ISO 8601 string (e.g., '2024-03-01 14:00' without seconds/zone, an epoch number, an Excel serial), or timeZone not a valid IANA name ('CST', 'America/ Chicago', empty). The earlier missing-fields check already flags absent values via conversionError + continue.

Common situations: Source systems emitting non-ISO datetimes (spreadsheets, legacy APIs), users typing 'CST'/'CDT' abbreviations instead of 'America/Chicago', or timezone strings with whitespace/typos. Downstream nodes must handle items carrying conversionError, otherwise 'Create Event' receives garbage times.

Related errors


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