Scenario behaves differently when run step-by-step

Describe the issue/error/question

I’m running a workflow, as attached (the HTTP node is detached so it doesn’t actually send any data)

When I’m running it by ‘Execute workflow’ it stops at the airtable list node, resulting with zero items,
but when I’m running it step-by-step it works as expected - returning my data.

What is the error message (if any)?

Workflow reports a success, no messages on the console

Share the output returned by the last node

none

workflow

{
  "name": "Create GSMT Tasks",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "airtable.baseId",
              "value": "redacted"
            },
            {
              "name": "airtable.servicePlan",
              "value": "redacted"
            },
            {
              "name": "gsm_tasks.account",
              "value": "redacted"
            }
          ],
          "boolean": []
        },
        "options": {}
      },
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        300,
        300
      ]
    },
    {
      "parameters": {
        "operation": "list",
        "application": "={{$json[\"airtable\"][\"baseId\"]}}",
        "table": "={{$json[\"airtable\"][\"servicePlan\"]}}",
        "additionalOptions": {
          "filterByFormula": "=AND(\n\tNOT(\n\t\tis_before({complete_after},datetime_parse('{{$json[\"start_date\"]}}','YYYY-MM-DDTHH:mm:ss.SSSZZ'))\n\t),\n\tis_before({complete_after},datetime_parse('{{$json[\"end_date\"]}}','YYYY-MM-DDTHH:mm:ss.SSSZZ'))\n)",
          "sort": {
            "property": [
              {
                "field": "Ordering"
              }
            ]
          }
        }
      },
      "name": "Get Service plan",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 1,
      "position": [
        700,
        300
      ],
      "notesInFlow": true,
      "credentials": {
        "airtableApi": {
          "id": "1",
          "name": "Airtable account"
        }
      },
      "notes": "complete_after >= start_date && complete_after < end_date\n"
    },
    {
      "parameters": {
        "authentication": "headerAuth",
        "requestMethod": "POST",
        "url": "https://api.gsmtasks.com/tasks/",
        "jsonParameters": true,
        "options": {
          "bodyContentType": "json",
          "fullResponse": true,
          "ignoreResponseCode": true
        },
        "bodyParametersJson": "={{$json}}"
      },
      "name": "Create GSMTasks task",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        1300,
        300
      ],
      "retryOnFail": false,
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "4",
          "name": "GSMTasks - Phillip"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "functionCode": "\nconsole.log(item);\n\nreturn item;"
      },
      "name": "Log task URL",
      "type": "n8n-nodes-base.functionItem",
      "typeVersion": 1,
      "position": [
        1700,
        360
      ]
    },
    {
      "parameters": {
        "functionCode": "var __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getOwnPropSymbols = Object.getOwnPropertySymbols;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __propIsEnum = Object.prototype.propertyIsEnumerable;\nvar __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\nvar __spreadValues = (a, b) => {\n  for (var prop in b || (b = {}))\n    if (__hasOwnProp.call(b, prop))\n      __defNormalProp(a, prop, b[prop]);\n  if (__getOwnPropSymbols)\n    for (var prop of __getOwnPropSymbols(b)) {\n      if (__propIsEnum.call(b, prop))\n        __defNormalProp(a, prop, b[prop]);\n    }\n  return a;\n};\nvar __markAsModule = (target) => __defProp(target, \"__esModule\", { value: true });\nvar __reExport = (target, module2, desc) => {\n  if (module2 && typeof module2 === \"object\" || typeof module2 === \"function\") {\n    for (let key of __getOwnPropNames(module2))\n      if (!__hasOwnProp.call(target, key) && key !== \"default\")\n        __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });\n  }\n  return target;\n};\nvar __toModule = (module2) => {\n  return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, \"default\", module2 && module2.__esModule && \"default\" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);\n};\n\n// src/create-gsmt-tasks/set-dates.ts\nvar dateFns = __toModule(require(\"date-fns\"));\nfunction getEndDate(config, start_date) {\n  if (config.end_date) {\n    return config.end_date;\n  } else {\n    const generate_days = (config.generate_days || 7);\n    return dateFns.addDays(start_date, generate_days);\n  }\n}\nfunction getDates(config) {\n  const start_date = config.start_date || dateFns.startOfTomorrow();\n  return {\n    start_date,\n    end_date: getEndDate(config, start_date)\n  };\n}\nfunction main(item2) {\n  return __spreadValues(__spreadValues({}, item2), getDates(item2));\n}\n\n// src/create-gsmt-tasks/set-dates.main.ts\n\n  return main(item);\n\n"
      },
      "name": "Set dates",
      "type": "n8n-nodes-base.functionItem",
      "typeVersion": 1,
      "position": [
        500,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"statusCode\"]}}",
              "operation": "largerEqual",
              "value2": 400
            }
          ]
        }
      },
      "name": "IF",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1500,
        300
      ]
    },
    {
      "parameters": {
        "errorMessage": "={{$json}}"
      },
      "name": "Stop And Error",
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        1700,
        220
      ]
    },
    {
      "parameters": {
        "functionCode": "// src/create-gsmt-tasks/convert-tasks.ts\nfunction extractValue(input) {\n  if (!!input && !!input[0])\n    return input[0];\n  else\n    return void 0;\n}\nfunction extractCoords(input) {\n  const long = input.longitude;\n  const lat = input.latitude;\n  if (lat && long) {\n    try {\n      const longf = Number.parseFloat(long[0]);\n      const latf = Number.parseFloat(lat[0]);\n      return latf && longf ? [longf, latf] : void 0;\n    } catch (e) {\n      console.log(`Could not parse coords: \"${lat}\", \"${long}\"`);\n      console.log(e);\n      return void 0;\n    }\n  }\n  return void 0;\n}\nfunction extractContact(input) {\n  const contactName = extractValue(input[\"Contact name\"]);\n  const contactEmail = extractValue(input[\"Contact email\"]);\n  const contactPhone = extractValue(input[\"Contact phone\"]);\n  const result = {};\n  if (contactEmail)\n    result.emails = [contactEmail];\n  if (contactPhone)\n    result.phones = [contactPhone];\n  return result || void 0;\n}\nfunction extractAssignee(item2, config) {\n  var _a;\n  const driverId = ((_a = config == null ? void 0 : config.override) == null ? void 0 : _a.assignee) || item2.fields.gsmt_user_id;\n  return driverId ? `https://api.gsmtasks.com/users/${driverId}/` : void 0;\n}\nfunction translate(item2, config) {\n  var _a, _b;\n  const input = item2.fields;\n  if (!input[\"location name\"])\n    throw new Error(\"Empty location name\");\n  const result = {\n    account: config.gsm_tasks.account,\n    assignee: extractAssignee(item2, config),\n    address: {\n      street: extractValue(input.Street),\n      city: extractValue(input.City),\n      country: \"\\xD6sterreich\",\n      postal_code: extractValue(input[\"Postal code\"])\n    },\n    description: input.Description,\n    metafields: {\n      [\"disp:loc_mnemo\"]: input[\"location name\"],\n      [\"disp:quick_details\"]: input[\"Quick details\"] || \"\",\n      [\"disp:quick_icons\"]: input[\"Quick icons\"] || \"\",\n      [\"disp:cat_icon\"]: input[\"Category icon\"]\n    },\n    duration: extractValue(input[\"Duration sec\"]),\n    reference: input.Name,\n    contact: extractContact(input)\n  };\n  result.address.raw_address = [(_a = result.address) == null ? void 0 : _a.street, (_b = result.address) == null ? void 0 : _b.city].filter((i) => i).join(\" \") || void 0;\n  const coords = extractCoords(input);\n  if (coords)\n    result.address.location = {\n      type: \"Point\",\n      coordinates: coords\n    };\n  return result;\n}\n\n// src/create-gsmt-tasks/convert-tasks.main.ts\nfunction main(){\n  return translate(item, $item(0).$node.Configuration.json);\n}\n\nreturn main();"
      },
      "name": "Convert task",
      "type": "n8n-nodes-base.functionItem",
      "typeVersion": 1,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "// src/create-gsmt-tasks/merge.main.ts\n\n  return [{ json: items.reduce((result, item) => {\n    result.push(item.json);\n    return result;\n  }, []) }];\n\n"
      },
      "name": "Merge",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1100,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration": {
      "main": [
        [
          {
            "node": "Set dates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Service plan": {
      "main": [
        [
          {
            "node": "Convert task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create GSMTasks task": {
      "main": [
        [
          {
            "node": "IF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set dates": {
      "main": [
        [
          {
            "node": "Get Service plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF": {
      "main": [
        [
          {
            "node": "Stop And Error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log task URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert task": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "timezone": "Europe/Vienna",
    "saveExecutionProgress": true,
    "saveManualExecutions": true,
    "errorWorkflow": "6"
  },
  "id": 3
}

Information on your n8n setup

  • n8n version: 0.155.2
  • Database you’re using (default: SQLite): mariadb 10.7
  • Running n8n with the execution process [own(default), main]: default
  • Running n8n via [Docker, npm, n8n.cloud, desktop app]: docker

If you execute step by step then n8n reuses the data n8n has currently saved in the browser which is “pure” JSON. Meaning dates are saved as strings. If you run via “Execute Workflow” then n8n currently passes through exactly what you give it, which is in your example some kind of Date-Object.

To fix that, you have to make sure that start_date and end_date are set as strings on the “Set dates” node, so that the filter you create with that data on the node “Get Service plan” is valid and returns the expected result.

Btw. here is a workflow that makes maybe clearer what I am talking about. You will see the difference of the data on the Set node depending on how you execute it.

{
  "nodes": [
    {
      "parameters": {
        "functionCode": "// Code here will run only once, no matter how many input items there are.\n// More info and help: https://docs.n8n.io/nodes/n8n-nodes-base.function\n\n// Loop over inputs and add a new field called 'myNewField' to the JSON of each one\nfor (item of items) {\n  item.json.dateObject = new Date();\n}\n\n// You can write logs to the browser console\nconsole.log('Done!');\n\nreturn items;"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "resultOnSet",
              "value": "={{$json[\"dateObject\"]}} [{{ typeof $json[\"dateObject\"]}}]"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        680,
        300
      ]
    }
  ],
  "connections": {
    "Function": {
      "main": [
        [
          {
            "node": "Set",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Just opened a PR to fix this issue in the future:

3 Likes

Thank you, that solves it.

Cheers