n8n-core 2.37.2 → 2.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/binary-data/blob.manager.d.ts +1 -0
  2. package/dist/binary-data/blob.manager.js +5 -0
  3. package/dist/binary-data/blob.manager.js.map +1 -1
  4. package/dist/binary-data/index.d.ts +1 -1
  5. package/dist/binary-data/index.js +2 -1
  6. package/dist/binary-data/index.js.map +1 -1
  7. package/dist/build.tsbuildinfo +1 -1
  8. package/dist/encryption/cipher.js.map +1 -1
  9. package/dist/execution-engine/node-execution-context/hook-context.js +2 -1
  10. package/dist/execution-engine/node-execution-context/hook-context.js.map +1 -1
  11. package/dist/execution-engine/node-execution-context/load-options-context.js +2 -1
  12. package/dist/execution-engine/node-execution-context/load-options-context.js.map +1 -1
  13. package/dist/execution-engine/node-execution-context/poll-context.d.ts +2 -1
  14. package/dist/execution-engine/node-execution-context/poll-context.js +5 -2
  15. package/dist/execution-engine/node-execution-context/poll-context.js.map +1 -1
  16. package/dist/execution-engine/node-execution-context/supply-data-context.d.ts +1 -1
  17. package/dist/execution-engine/node-execution-context/supply-data-context.js +2 -0
  18. package/dist/execution-engine/node-execution-context/supply-data-context.js.map +1 -1
  19. package/dist/execution-engine/node-execution-context/trigger-context.js +2 -1
  20. package/dist/execution-engine/node-execution-context/trigger-context.js.map +1 -1
  21. package/dist/execution-engine/node-execution-context/utils/get-input-connection-data.js +3 -2
  22. package/dist/execution-engine/node-execution-context/utils/get-input-connection-data.js.map +1 -1
  23. package/dist/execution-engine/node-execution-context/utils/request-helpers/authentication.js +9 -9
  24. package/dist/execution-engine/node-execution-context/utils/request-helpers/authentication.js.map +1 -1
  25. package/dist/execution-engine/node-execution-context/utils/request-helpers/factory.js +2 -1
  26. package/dist/execution-engine/node-execution-context/utils/request-helpers/factory.js.map +1 -1
  27. package/dist/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.js +1 -3
  28. package/dist/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.js.map +1 -1
  29. package/dist/execution-engine/node-execution-context/utils/request-helpers/oauth.js +44 -3
  30. package/dist/execution-engine/node-execution-context/utils/request-helpers/oauth.js.map +1 -1
  31. package/dist/execution-engine/node-execution-context/webhook-context.d.ts +1 -0
  32. package/dist/execution-engine/node-execution-context/webhook-context.js +5 -1
  33. package/dist/execution-engine/node-execution-context/webhook-context.js.map +1 -1
  34. package/dist/execution-engine/workflow-execute.d.ts +25 -0
  35. package/dist/execution-engine/workflow-execute.js +375 -309
  36. package/dist/execution-engine/workflow-execute.js.map +1 -1
  37. package/package.json +17 -17
@@ -204,7 +204,7 @@ class WorkflowExecute {
204
204
  taskData.metadata = { ...taskData.metadata, ...metaRunData };
205
205
  }
206
206
  else {
207
- di_1.Container.get(error_reporter_1.ErrorReporter).error(new n8n_workflow_1.UnexpectedError('Taskdata missing at the end of an execution'), { extra: { nodeName, index } });
207
+ di_1.Container.get(error_reporter_1.ErrorReporter).error(new n8n_workflow_1.UnexpectedError('Task data missing at the end of an execution'), { extra: { nodeName, index } });
208
208
  }
209
209
  }
210
210
  }
@@ -799,6 +799,343 @@ class WorkflowExecute {
799
799
  this.addNodeToBeExecuted(workflow, nodeData.inputConnectionData, nodeData.parentOutputIndex, nodeData.parentNode, nodeData.parentOutputData, nodeData.runIndex, nodeData.nodeRunIndex, nodeData.metadata);
800
800
  }
801
801
  }
802
+ setupCancellation(onCancel, hooks, startedAt) {
803
+ (0, events_1.setMaxListeners)(Infinity, this.abortController.signal);
804
+ onCancel.shouldReject = false;
805
+ onCancel(() => {
806
+ this.status = 'canceled';
807
+ this.updateTaskStatusesToCancelled();
808
+ this.abortController.abort();
809
+ const fullRunData = this.getFullRunData(startedAt);
810
+ void hooks.runHook('workflowExecuteAfter', [fullRunData]);
811
+ });
812
+ }
813
+ async initializeExecution(workflow, hooks) {
814
+ try {
815
+ await workflow.expression.acquireIsolate();
816
+ await (0, execution_context_1.establishExecutionContext)(workflow, this.runExecutionData, this.additionalData, this.mode);
817
+ if (!this.additionalData.restartExecutionId) {
818
+ await hooks.runHook('workflowExecuteBefore', [workflow, this.runExecutionData]);
819
+ }
820
+ else {
821
+ await hooks.runHook('workflowExecuteResume', [workflow, this.runExecutionData]);
822
+ }
823
+ }
824
+ catch (error) {
825
+ const e = error;
826
+ const executionError = {
827
+ ...e,
828
+ message: e.message,
829
+ stack: e.stack,
830
+ };
831
+ const startItem = this.runExecutionData.executionData.nodeExecutionStack.at(0);
832
+ if (startItem) {
833
+ const taskData = {
834
+ startTime: Date.now(),
835
+ executionIndex: 0,
836
+ executionTime: 0,
837
+ data: {
838
+ main: startItem.data.main,
839
+ },
840
+ source: [],
841
+ executionStatus: 'error',
842
+ hints: [],
843
+ };
844
+ this.runExecutionData.resultData = {
845
+ runData: {
846
+ [startItem.node.name]: [taskData],
847
+ },
848
+ lastNodeExecuted: startItem.node.name,
849
+ error: executionError,
850
+ };
851
+ }
852
+ else {
853
+ this.runExecutionData.resultData.error = executionError;
854
+ }
855
+ throw error;
856
+ }
857
+ }
858
+ resetDynamicCredentialsUsage(executionData) {
859
+ this.additionalData.currentNodeUsedDynamicCredentials = false;
860
+ this.additionalData.currentNodeAttemptedDynamicCredentials = false;
861
+ const reportedUsage = executionData.metadata?.dynamicCredentialsUsage;
862
+ if (reportedUsage) {
863
+ (0, n8n_workflow_1.applyDynamicCredentialsUsage)(this.additionalData, reportedUsage);
864
+ delete executionData.metadata?.dynamicCredentialsUsage;
865
+ }
866
+ }
867
+ createTaskStartedData(executionData) {
868
+ return {
869
+ startTime: Date.now(),
870
+ executionIndex: this.additionalData.currentNodeExecutionIndex++,
871
+ source: !executionData.source ? [] : executionData.source.main,
872
+ hints: [],
873
+ };
874
+ }
875
+ addPairedItemLineage(executionData) {
876
+ const newTaskDataConnections = {};
877
+ for (const connectionType of Object.keys(executionData.data)) {
878
+ newTaskDataConnections[connectionType] = executionData.data[connectionType].map((input, inputIndex) => {
879
+ if (input === null) {
880
+ return input;
881
+ }
882
+ return input.map((item, itemIndex) => {
883
+ const sourceOverwrite = (0, node_execution_context_1.resolveSourceOverwrite)(item, executionData);
884
+ if (sourceOverwrite) {
885
+ return {
886
+ ...item,
887
+ pairedItem: {
888
+ item: itemIndex,
889
+ input: inputIndex || undefined,
890
+ sourceOverwrite,
891
+ },
892
+ };
893
+ }
894
+ return {
895
+ ...item,
896
+ pairedItem: {
897
+ item: itemIndex,
898
+ input: inputIndex || undefined,
899
+ },
900
+ };
901
+ });
902
+ });
903
+ }
904
+ return newTaskDataConnections;
905
+ }
906
+ computeRunIndex(executionData) {
907
+ if (executionData.runIndex !== undefined) {
908
+ return executionData.runIndex;
909
+ }
910
+ const { runData } = this.runExecutionData.resultData;
911
+ const nodeName = executionData.node.name;
912
+ return Object.hasOwn(runData, nodeName) ? runData[nodeName].length : 0;
913
+ }
914
+ getRetryParams(executionData) {
915
+ const isResumedError = executionData.metadata?.resumeError !== undefined;
916
+ if (executionData.node.retryOnFail !== true || isResumedError) {
917
+ return [1, 0];
918
+ }
919
+ return [
920
+ Math.min(5, Math.max(2, executionData.node.maxTries || 3)),
921
+ Math.min(5000, Math.max(0, executionData.node.waitBetweenTries || 1000)),
922
+ ];
923
+ }
924
+ getPinnedOutput(node) {
925
+ const { pinData } = this.runExecutionData.resultData;
926
+ if (!pinData || node.disabled || pinData[node.name] === undefined) {
927
+ return undefined;
928
+ }
929
+ return [pinData[node.name]];
930
+ }
931
+ collectSubNodeResults(executionData, subNodeExecutionResults) {
932
+ const { subNodeExecutionData } = executionData.metadata ?? {};
933
+ if (!subNodeExecutionData)
934
+ return;
935
+ subNodeExecutionResults.metadata = subNodeExecutionData.metadata;
936
+ for (const subNode of subNodeExecutionData.actions) {
937
+ const nodeRunData = this.runExecutionData.resultData.runData[subNode.nodeName];
938
+ if (nodeRunData?.[subNode.runIndex]) {
939
+ subNodeExecutionResults.actionResponses.push({
940
+ data: nodeRunData[subNode.runIndex],
941
+ action: subNode.action,
942
+ });
943
+ }
944
+ }
945
+ }
946
+ async processNodeOutput(runNodeData, workflow, executionData, taskStartedData, runIndex) {
947
+ const converted = await (0, convert_binary_data_1.convertBinaryData)(workflow.id, this.additionalData.executionId, runNodeData, workflow.settings.binaryMode);
948
+ const nodeSuccessData = converted.data;
949
+ if (converted.hints?.length) {
950
+ taskStartedData.hints.push(...converted.hints);
951
+ }
952
+ if (nodeSuccessData && executionData.node.onError === 'continueErrorOutput') {
953
+ this.handleNodeErrorOutput(workflow, executionData, nodeSuccessData, runIndex);
954
+ }
955
+ const closeFunction = converted.closeFunction?.();
956
+ return { nodeSuccessData, closeFunction };
957
+ }
958
+ buildErrorReport(error) {
959
+ if (error instanceof n8n_workflow_1.ApplicationError) {
960
+ return error.cause instanceof Error ? error.cause : undefined;
961
+ }
962
+ if (error instanceof n8n_workflow_1.BaseError) {
963
+ return error;
964
+ }
965
+ if (!(error instanceof Error) || (0, backend_network_1.isAxiosError)(error)) {
966
+ return undefined;
967
+ }
968
+ const errorClass = error.name || 'Error';
969
+ const sanitized = new Error(errorClass);
970
+ sanitized.name = errorClass;
971
+ const frames = (error.stack ?? '').split('\n').filter((line) => /^\s+at\s/.test(line));
972
+ sanitized.stack = [errorClass, ...frames].join('\n');
973
+ return sanitized;
974
+ }
975
+ reportNodeExecutionError(error, executionNode, workflow) {
976
+ this.runExecutionData.resultData.lastNodeExecuted = executionNode.name;
977
+ const toReport = this.buildErrorReport(error);
978
+ if (toReport) {
979
+ const { executionId, instanceBaseUrl } = this.additionalData;
980
+ di_1.Container.get(error_reporter_1.ErrorReporter).error(toReport, {
981
+ extra: {
982
+ nodeName: executionNode.name,
983
+ nodeType: executionNode.type,
984
+ nodeVersion: executionNode.typeVersion,
985
+ workflowId: workflow.id,
986
+ executionId,
987
+ executionUrl: instanceBaseUrl && executionId
988
+ ? `${instanceBaseUrl}workflow/${workflow.id}/executions/${executionId}`
989
+ : undefined,
990
+ },
991
+ });
992
+ }
993
+ n8n_workflow_1.LoggerProxy.debug(`Running node "${executionNode.name}" finished with error`, {
994
+ node: executionNode.name,
995
+ workflowId: workflow.id,
996
+ });
997
+ const e = normalizeUnhandledAxiosError(error, executionNode);
998
+ return { ...e, message: e.message, stack: e.stack };
999
+ }
1000
+ ensureAlwaysOutputData(nodeSuccessData, executionData) {
1001
+ if (nodeSuccessData?.[0]?.[0])
1002
+ return nodeSuccessData;
1003
+ if (executionData.node.alwaysOutputData !== true)
1004
+ return nodeSuccessData;
1005
+ const pairedItem = [];
1006
+ executionData.data.main.forEach((inputData, inputIndex) => {
1007
+ if (!inputData) {
1008
+ return;
1009
+ }
1010
+ inputData.forEach((_item, itemIndex) => {
1011
+ pairedItem.push({ item: itemIndex, input: inputIndex });
1012
+ });
1013
+ });
1014
+ nodeSuccessData ??= [];
1015
+ nodeSuccessData[0] = [{ json: {}, pairedItem }];
1016
+ return nodeSuccessData;
1017
+ }
1018
+ createTaskData(taskStartedData, executionData) {
1019
+ return {
1020
+ ...taskStartedData,
1021
+ executionTime: Date.now() - taskStartedData.startTime,
1022
+ metadata: executionData.metadata,
1023
+ executionStatus: this.runExecutionData.waitTill ? 'waiting' : 'success',
1024
+ usedDynamicCredentials: this.additionalData.currentNodeUsedDynamicCredentials || undefined,
1025
+ attemptedDynamicCredentials: this.additionalData.currentNodeAttemptedDynamicCredentials || undefined,
1026
+ };
1027
+ }
1028
+ recordDynamicCredentialsUser() {
1029
+ if (this.additionalData.currentNodeUsedDynamicCredentials &&
1030
+ this.additionalData.dynamicCredentialsResolvedUserId &&
1031
+ this.runExecutionData.executionData?.runtimeData) {
1032
+ this.runExecutionData.executionData.runtimeData.executedByUserId =
1033
+ this.additionalData.dynamicCredentialsResolvedUserId;
1034
+ }
1035
+ }
1036
+ async handleNodeExecutionError({ executionNode, executionData, taskData, executionError, nodeSuccessData, runIndex, hooks, }) {
1037
+ taskData.error = executionError;
1038
+ taskData.executionStatus = 'error';
1039
+ await hooks?.runHook('sendChunk', [
1040
+ {
1041
+ type: 'error',
1042
+ content: executionError.description,
1043
+ metadata: {
1044
+ nodeId: executionNode.id,
1045
+ nodeName: executionNode.name,
1046
+ runIndex,
1047
+ itemIndex: 0,
1048
+ },
1049
+ },
1050
+ ]);
1051
+ const isAiToolExecution = executionNode.rewireOutputLogTo === n8n_workflow_1.NodeConnectionTypes.AiTool;
1052
+ const aiToolDefaultsToContinue = isAiToolExecution && executionData.node.onError !== 'stopWorkflow';
1053
+ if (this.continuesOnError(executionData.node) || aiToolDefaultsToContinue) {
1054
+ if (isAiToolExecution) {
1055
+ nodeSuccessData = [[{ json: { error: executionError.message } }]];
1056
+ }
1057
+ else if (Object.hasOwn(executionData.data, 'main') && executionData.data.main.length > 0) {
1058
+ if (executionData.data.main[0] !== null) {
1059
+ nodeSuccessData = [executionData.data.main[0]];
1060
+ }
1061
+ }
1062
+ return { continueExecution: true, nodeSuccessData };
1063
+ }
1064
+ if (executionNode.rewireOutputLogTo) {
1065
+ taskData.inputOverride =
1066
+ this.runExecutionData.resultData.runData[executionNode.name][runIndex]?.inputOverride || {};
1067
+ taskData.data = {
1068
+ [executionNode.rewireOutputLogTo]: [[{ json: { error: executionError.message } }]],
1069
+ };
1070
+ }
1071
+ this.upsertTaskData(executionNode.name, runIndex, taskData);
1072
+ this.pushExecutionStack(executionData);
1073
+ if (!this.isCancelled) {
1074
+ await hooks.runHook('nodeExecuteAfter', [
1075
+ executionNode.name,
1076
+ taskData,
1077
+ this.runExecutionData,
1078
+ ]);
1079
+ }
1080
+ return { continueExecution: false, nodeSuccessData };
1081
+ }
1082
+ upsertTaskData(nodeName, runIndex, taskData) {
1083
+ const nodeRunData = this.runExecutionData.resultData.runData[nodeName];
1084
+ if (nodeRunData[runIndex]) {
1085
+ Object.assign(nodeRunData[runIndex], taskData);
1086
+ }
1087
+ else {
1088
+ nodeRunData.push(taskData);
1089
+ }
1090
+ }
1091
+ normalizeNodeErrors(nodeSuccessData) {
1092
+ for (const execution of nodeSuccessData) {
1093
+ for (const lineResult of execution) {
1094
+ if (lineResult.json !== undefined &&
1095
+ lineResult.json.$error !== undefined &&
1096
+ lineResult.json.$json !== undefined) {
1097
+ lineResult.error = lineResult.json.$error;
1098
+ lineResult.json = { error: lineResult.error.message };
1099
+ }
1100
+ else if (lineResult.error !== undefined) {
1101
+ lineResult.json = { error: lineResult.error.message };
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+ rewireOutputLog(executionNode, taskData, nodeSuccessData, runIndex) {
1107
+ if (!executionNode.rewireOutputLogTo)
1108
+ return;
1109
+ taskData.inputOverride =
1110
+ this.runExecutionData.resultData.runData[executionNode.name]?.[runIndex]?.inputOverride || {};
1111
+ taskData.data = {
1112
+ [executionNode.rewireOutputLogTo]: nodeSuccessData,
1113
+ };
1114
+ }
1115
+ isExecutionStackNotEmpty() {
1116
+ return this.runExecutionData.executionData.nodeExecutionStack.length !== 0;
1117
+ }
1118
+ popExecutionStack() {
1119
+ return this.runExecutionData.executionData.nodeExecutionStack.shift();
1120
+ }
1121
+ pushExecutionStack(executionData) {
1122
+ this.runExecutionData.executionData.nodeExecutionStack.unshift(executionData);
1123
+ }
1124
+ hasExecutionTimedOut() {
1125
+ return (this.additionalData.executionTimeoutTimestamp !== undefined &&
1126
+ Date.now() >= this.additionalData.executionTimeoutTimestamp);
1127
+ }
1128
+ shouldStopExecuting() {
1129
+ if (this.hasExecutionTimedOut()) {
1130
+ this.status = 'canceled';
1131
+ this.timedOut = true;
1132
+ }
1133
+ return this.status === 'canceled';
1134
+ }
1135
+ isNodeFilteredOut(nodeName) {
1136
+ const { runNodeFilter } = this.runExecutionData.startData;
1137
+ return runNodeFilter !== undefined && !runNodeFilter.includes(nodeName);
1138
+ }
802
1139
  processRunExecutionData(workflow) {
803
1140
  n8n_workflow_1.LoggerProxy.debug('Workflow execution started', { workflowId: workflow.id });
804
1141
  const { startedAt, hooks } = this.setupExecution();
@@ -813,124 +1150,27 @@ class WorkflowExecute {
813
1150
  let lastExecutionTry = '';
814
1151
  let closeFunction;
815
1152
  return new p_cancelable_1.default(async (resolve, _reject, onCancel) => {
816
- (0, events_1.setMaxListeners)(Infinity, this.abortController.signal);
817
- onCancel.shouldReject = false;
818
- onCancel(() => {
819
- this.status = 'canceled';
820
- this.updateTaskStatusesToCancelled();
821
- this.abortController.abort();
822
- const fullRunData = this.getFullRunData(startedAt);
823
- void hooks.runHook('workflowExecuteAfter', [fullRunData]);
824
- });
1153
+ this.setupCancellation(onCancel, hooks, startedAt);
825
1154
  const returnPromise = (async () => {
826
- try {
827
- await workflow.expression.acquireIsolate();
828
- await (0, execution_context_1.establishExecutionContext)(workflow, this.runExecutionData, this.additionalData, this.mode);
829
- if (!this.additionalData.restartExecutionId) {
830
- await hooks.runHook('workflowExecuteBefore', [workflow, this.runExecutionData]);
831
- }
832
- else {
833
- await hooks.runHook('workflowExecuteResume', [workflow, this.runExecutionData]);
834
- }
835
- }
836
- catch (error) {
837
- const e = error;
838
- executionError = {
839
- ...e,
840
- message: e.message,
841
- stack: e.stack,
842
- };
843
- executionData = this.runExecutionData.executionData.nodeExecutionStack[0];
844
- const taskData = {
845
- startTime: Date.now(),
846
- executionIndex: 0,
847
- executionTime: 0,
848
- data: {
849
- main: executionData.data.main,
850
- },
851
- source: [],
852
- executionStatus: 'error',
853
- hints: [],
854
- };
855
- this.runExecutionData.resultData = {
856
- runData: {
857
- [executionData.node.name]: [taskData],
858
- },
859
- lastNodeExecuted: executionData.node.name,
860
- error: executionError,
861
- };
862
- throw error;
863
- }
864
- executionLoop: while (this.runExecutionData.executionData.nodeExecutionStack.length !== 0) {
865
- if (this.additionalData.executionTimeoutTimestamp !== undefined &&
866
- Date.now() >= this.additionalData.executionTimeoutTimestamp) {
867
- this.status = 'canceled';
868
- this.timedOut = true;
869
- }
870
- if (this.status === 'canceled') {
1155
+ await this.initializeExecution(workflow, hooks);
1156
+ executionLoop: while (this.isExecutionStackNotEmpty()) {
1157
+ if (this.shouldStopExecuting()) {
871
1158
  return;
872
1159
  }
873
1160
  subNodeExecutionResults = (0, requests_response_1.makeEngineResponse)();
874
1161
  let nodeSuccessData = null;
875
1162
  executionError = undefined;
876
- executionData =
877
- this.runExecutionData.executionData.nodeExecutionStack.shift();
1163
+ executionData = this.popExecutionStack();
878
1164
  executionNode = executionData.node;
879
- this.additionalData.currentNodeUsedDynamicCredentials = false;
880
- this.additionalData.currentNodeAttemptedDynamicCredentials = false;
881
- const reportedUsage = executionData.metadata?.dynamicCredentialsUsage;
882
- if (reportedUsage) {
883
- (0, n8n_workflow_1.applyDynamicCredentialsUsage)(this.additionalData, reportedUsage);
884
- delete executionData.metadata?.dynamicCredentialsUsage;
885
- }
886
- const taskStartedData = {
887
- startTime: Date.now(),
888
- executionIndex: this.additionalData.currentNodeExecutionIndex++,
889
- source: !executionData.source ? [] : executionData.source.main,
890
- hints: [],
891
- };
892
- const newTaskDataConnections = {};
893
- for (const connectionType of Object.keys(executionData.data)) {
894
- newTaskDataConnections[connectionType] = executionData.data[connectionType].map((input, inputIndex) => {
895
- if (input === null) {
896
- return input;
897
- }
898
- return input.map((item, itemIndex) => {
899
- const sourceOverwrite = (0, node_execution_context_1.resolveSourceOverwrite)(item, executionData);
900
- if (sourceOverwrite) {
901
- return {
902
- ...item,
903
- pairedItem: {
904
- item: itemIndex,
905
- input: inputIndex || undefined,
906
- sourceOverwrite,
907
- },
908
- };
909
- }
910
- return {
911
- ...item,
912
- pairedItem: {
913
- item: itemIndex,
914
- input: inputIndex || undefined,
915
- },
916
- };
917
- });
918
- });
919
- }
920
- executionData.data = newTaskDataConnections;
921
- runIndex = 0;
922
- if (executionData.runIndex !== undefined) {
923
- runIndex = executionData.runIndex;
924
- }
925
- else if (Object.hasOwn(this.runExecutionData.resultData.runData, executionNode.name)) {
926
- runIndex = this.runExecutionData.resultData.runData[executionNode.name].length;
927
- }
1165
+ this.resetDynamicCredentialsUsage(executionData);
1166
+ const taskStartedData = this.createTaskStartedData(executionData);
1167
+ executionData.data = this.addPairedItemLineage(executionData);
1168
+ runIndex = this.computeRunIndex(executionData);
928
1169
  currentExecutionTry = `${executionNode.name}:${runIndex}`;
929
1170
  if (currentExecutionTry === lastExecutionTry) {
930
1171
  throw new n8n_workflow_1.UserError('Stopped execution because it seems to be in an endless loop');
931
1172
  }
932
- if (this.runExecutionData.startData.runNodeFilter !== undefined &&
933
- this.runExecutionData.startData.runNodeFilter.indexOf(executionNode.name) === -1) {
1173
+ if (this.isNodeFilteredOut(executionNode.name)) {
934
1174
  continue;
935
1175
  }
936
1176
  const hasInputData = this.ensureInputData(workflow, executionNode, executionData);
@@ -945,17 +1185,9 @@ class WorkflowExecute {
945
1185
  if (!executionData.metadata?.nodeWasResumed) {
946
1186
  await hooks.runHook('nodeExecuteBefore', [executionNode.name, taskStartedData]);
947
1187
  }
948
- let maxTries = 1;
949
1188
  const isErrorValue = (v) => v !== undefined && v !== null && v !== false;
950
1189
  const checkFailure = (data) => !(0, requests_response_1.isEngineRequest)(data) && isErrorValue(data.data?.[0]?.[0]?.json?.error);
951
- const isResumedError = executionData.metadata?.resumeError !== undefined;
952
- if (executionData.node.retryOnFail === true && !isResumedError) {
953
- maxTries = Math.min(5, Math.max(2, executionData.node.maxTries || 3));
954
- }
955
- let waitBetweenTries = 0;
956
- if (executionData.node.retryOnFail === true && !isResumedError) {
957
- waitBetweenTries = Math.min(5000, Math.max(0, executionData.node.waitBetweenTries || 1000));
958
- }
1190
+ const [maxTries, waitBetweenTries] = this.getRetryParams(executionData);
959
1191
  for (let tryIndex = 0; tryIndex < maxTries; tryIndex++) {
960
1192
  try {
961
1193
  if (tryIndex !== 0) {
@@ -968,26 +1200,12 @@ class WorkflowExecute {
968
1200
  });
969
1201
  }
970
1202
  }
971
- const { pinData } = this.runExecutionData.resultData;
972
- if (pinData && !executionNode.disabled && pinData[executionNode.name] !== undefined) {
973
- const nodePinData = pinData[executionNode.name];
974
- nodeSuccessData = [nodePinData];
1203
+ const pinnedOutput = this.getPinnedOutput(executionNode);
1204
+ if (pinnedOutput) {
1205
+ nodeSuccessData = pinnedOutput;
975
1206
  }
976
1207
  else {
977
- if (executionData.metadata?.subNodeExecutionData) {
978
- subNodeExecutionResults.metadata =
979
- executionData.metadata.subNodeExecutionData.metadata;
980
- for (const subNode of executionData.metadata.subNodeExecutionData.actions) {
981
- const nodeRunData = this.runExecutionData.resultData.runData[subNode.nodeName];
982
- if (nodeRunData && nodeRunData[subNode.runIndex]) {
983
- const data = nodeRunData[subNode.runIndex];
984
- subNodeExecutionResults.actionResponses.push({
985
- data,
986
- action: subNode.action,
987
- });
988
- }
989
- }
990
- }
1208
+ this.collectSubNodeResults(executionData, subNodeExecutionResults);
991
1209
  n8n_workflow_1.LoggerProxy.debug(`Running node "${executionNode.name}" started`, {
992
1210
  node: executionNode.name,
993
1211
  workflowId: workflow.id,
@@ -1011,17 +1229,9 @@ class WorkflowExecute {
1011
1229
  });
1012
1230
  continue executionLoop;
1013
1231
  }
1014
- runNodeData = await (0, convert_binary_data_1.convertBinaryData)(workflow.id, this.additionalData.executionId, runNodeData, workflow.settings.binaryMode);
1015
- nodeSuccessData = runNodeData.data;
1016
- if (runNodeData.hints?.length) {
1017
- taskStartedData.hints.push.apply(taskStartedData.hints, runNodeData.hints);
1018
- }
1019
- if (nodeSuccessData && executionData.node.onError === 'continueErrorOutput') {
1020
- this.handleNodeErrorOutput(workflow, executionData, nodeSuccessData, runIndex);
1021
- }
1022
- if (runNodeData.closeFunction) {
1023
- closeFunction = runNodeData.closeFunction();
1024
- }
1232
+ const nodeOutput = await this.processNodeOutput(runNodeData, workflow, executionData, taskStartedData, runIndex);
1233
+ nodeSuccessData = nodeOutput.nodeSuccessData;
1234
+ closeFunction = nodeOutput.closeFunction ?? closeFunction;
1025
1235
  }
1026
1236
  n8n_workflow_1.LoggerProxy.debug(`Running node "${executionNode.name}" finished successfully`, {
1027
1237
  node: executionNode.name,
@@ -1031,193 +1241,49 @@ class WorkflowExecute {
1031
1241
  if (nodeSuccessData) {
1032
1242
  this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
1033
1243
  }
1034
- if (!nodeSuccessData?.[0]?.[0]) {
1035
- if (executionData.node.alwaysOutputData === true) {
1036
- const pairedItem = [];
1037
- executionData.data.main.forEach((inputData, inputIndex) => {
1038
- if (!inputData) {
1039
- return;
1040
- }
1041
- inputData.forEach((_item, itemIndex) => {
1042
- pairedItem.push({
1043
- item: itemIndex,
1044
- input: inputIndex,
1045
- });
1046
- });
1047
- });
1048
- nodeSuccessData ??= [];
1049
- nodeSuccessData[0] = [
1050
- {
1051
- json: {},
1052
- pairedItem,
1053
- },
1054
- ];
1055
- }
1056
- }
1244
+ nodeSuccessData = this.ensureAlwaysOutputData(nodeSuccessData, executionData);
1057
1245
  if (nodeSuccessData === null && !this.runExecutionData.waitTill) {
1058
1246
  continue executionLoop;
1059
1247
  }
1060
1248
  break;
1061
1249
  }
1062
1250
  catch (error) {
1063
- this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
1064
- let toReport;
1065
- if (error instanceof n8n_workflow_1.ApplicationError) {
1066
- if (error.cause instanceof Error)
1067
- toReport = error.cause;
1068
- }
1069
- else if (error instanceof n8n_workflow_1.BaseError) {
1070
- toReport = error;
1071
- }
1072
- else if (error instanceof Error && !(0, backend_network_1.isAxiosError)(error)) {
1073
- const errorClass = error.name || 'Error';
1074
- const sanitized = new Error(errorClass);
1075
- sanitized.name = errorClass;
1076
- const frames = (error.stack ?? '')
1077
- .split('\n')
1078
- .filter((line) => /^\s+at\s/.test(line));
1079
- sanitized.stack = [errorClass, ...frames].join('\n');
1080
- toReport = sanitized;
1081
- }
1082
- if (toReport) {
1083
- const { executionId, instanceBaseUrl } = this.additionalData;
1084
- di_1.Container.get(error_reporter_1.ErrorReporter).error(toReport, {
1085
- extra: {
1086
- nodeName: executionNode.name,
1087
- nodeType: executionNode.type,
1088
- nodeVersion: executionNode.typeVersion,
1089
- workflowId: workflow.id,
1090
- executionId,
1091
- executionUrl: instanceBaseUrl && executionId
1092
- ? `${instanceBaseUrl}workflow/${workflow.id}/executions/${executionId}`
1093
- : undefined,
1094
- },
1095
- });
1096
- }
1097
- const e = normalizeUnhandledAxiosError(error, executionNode);
1098
- executionError = { ...e, message: e.message, stack: e.stack };
1099
- n8n_workflow_1.LoggerProxy.debug(`Running node "${executionNode.name}" finished with error`, {
1100
- node: executionNode.name,
1101
- workflowId: workflow.id,
1102
- });
1251
+ executionError = this.reportNodeExecutionError(error, executionNode, workflow);
1103
1252
  }
1104
1253
  }
1105
1254
  if (!Object.hasOwn(this.runExecutionData.resultData.runData, executionNode.name)) {
1106
1255
  this.runExecutionData.resultData.runData[executionNode.name] = [];
1107
1256
  }
1108
- const taskData = {
1109
- ...taskStartedData,
1110
- executionTime: Date.now() - taskStartedData.startTime,
1111
- metadata: executionData.metadata,
1112
- executionStatus: this.runExecutionData.waitTill ? 'waiting' : 'success',
1113
- usedDynamicCredentials: this.additionalData.currentNodeUsedDynamicCredentials || undefined,
1114
- attemptedDynamicCredentials: this.additionalData.currentNodeAttemptedDynamicCredentials || undefined,
1115
- };
1116
- if (this.additionalData.currentNodeUsedDynamicCredentials &&
1117
- this.additionalData.dynamicCredentialsResolvedUserId &&
1118
- this.runExecutionData.executionData?.runtimeData) {
1119
- this.runExecutionData.executionData.runtimeData.executedByUserId =
1120
- this.additionalData.dynamicCredentialsResolvedUserId;
1121
- }
1257
+ const taskData = this.createTaskData(taskStartedData, executionData);
1258
+ this.recordDynamicCredentialsUser();
1122
1259
  if (executionError !== undefined) {
1123
- taskData.error = executionError;
1124
- taskData.executionStatus = 'error';
1125
- await hooks?.runHook('sendChunk', [
1126
- {
1127
- type: 'error',
1128
- content: executionError.description,
1129
- metadata: {
1130
- nodeId: executionNode.id,
1131
- nodeName: executionNode.name,
1132
- runIndex,
1133
- itemIndex: 0,
1134
- },
1135
- },
1136
- ]);
1137
- const isAiToolExecution = executionNode.rewireOutputLogTo === n8n_workflow_1.NodeConnectionTypes.AiTool;
1138
- const aiToolDefaultsToContinue = isAiToolExecution && executionData.node.onError !== 'stopWorkflow';
1139
- if (this.continuesOnError(executionData.node) || aiToolDefaultsToContinue) {
1140
- if (isAiToolExecution) {
1141
- nodeSuccessData = [[{ json: { error: executionError.message } }]];
1142
- }
1143
- else if (Object.hasOwn(executionData.data, 'main') &&
1144
- executionData.data.main.length > 0) {
1145
- if (executionData.data.main[0] !== null) {
1146
- nodeSuccessData = [executionData.data.main[0]];
1147
- }
1148
- }
1149
- }
1150
- else {
1151
- if (executionNode.rewireOutputLogTo) {
1152
- taskData.inputOverride =
1153
- this.runExecutionData.resultData.runData[executionNode.name][runIndex]
1154
- ?.inputOverride || {};
1155
- taskData.data = {
1156
- [executionNode.rewireOutputLogTo]: [
1157
- [{ json: { error: executionError.message } }],
1158
- ],
1159
- };
1160
- }
1161
- const errorRunDataExists = !!this.runExecutionData.resultData.runData[executionNode.name][runIndex];
1162
- if (errorRunDataExists) {
1163
- const currentTaskData = this.runExecutionData.resultData.runData[executionNode.name][runIndex];
1164
- Object.assign(currentTaskData, taskData);
1165
- }
1166
- else {
1167
- this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
1168
- }
1169
- this.runExecutionData.executionData.nodeExecutionStack.unshift(executionData);
1170
- if (!this.isCancelled) {
1171
- await hooks.runHook('nodeExecuteAfter', [
1172
- executionNode.name,
1173
- taskData,
1174
- this.runExecutionData,
1175
- ]);
1176
- }
1260
+ const outcome = await this.handleNodeExecutionError({
1261
+ executionNode,
1262
+ executionData,
1263
+ taskData,
1264
+ executionError,
1265
+ nodeSuccessData,
1266
+ runIndex,
1267
+ hooks,
1268
+ });
1269
+ nodeSuccessData = outcome.nodeSuccessData;
1270
+ if (!outcome.continueExecution) {
1177
1271
  break;
1178
1272
  }
1179
1273
  }
1180
- for (const execution of nodeSuccessData) {
1181
- for (const lineResult of execution) {
1182
- if (lineResult.json !== undefined &&
1183
- lineResult.json.$error !== undefined &&
1184
- lineResult.json.$json !== undefined) {
1185
- lineResult.error = lineResult.json.$error;
1186
- lineResult.json = {
1187
- error: lineResult.json.$error.message,
1188
- };
1189
- }
1190
- else if (lineResult.error !== undefined) {
1191
- lineResult.json = { error: lineResult.error.message };
1192
- }
1193
- }
1194
- }
1274
+ this.normalizeNodeErrors(nodeSuccessData);
1195
1275
  taskData.data = {
1196
1276
  main: nodeSuccessData,
1197
1277
  };
1198
- if (executionNode.rewireOutputLogTo) {
1199
- taskData.inputOverride =
1200
- this.runExecutionData.resultData.runData[executionNode.name]?.[runIndex]
1201
- ?.inputOverride || {};
1202
- taskData.data = {
1203
- [executionNode.rewireOutputLogTo]: nodeSuccessData,
1204
- };
1205
- }
1206
- const runDataAlreadyExists = !!this.runExecutionData.resultData.runData[executionNode.name][runIndex];
1207
- if (runDataAlreadyExists) {
1208
- const currentTaskData = this.runExecutionData.resultData.runData[executionNode.name][runIndex];
1209
- Object.assign(currentTaskData, taskData);
1210
- }
1211
- else {
1212
- this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
1213
- }
1278
+ this.rewireOutputLog(executionNode, taskData, nodeSuccessData, runIndex);
1279
+ this.upsertTaskData(executionNode.name, runIndex, taskData);
1214
1280
  if (this.runExecutionData.waitTill) {
1215
1281
  await hooks.runHook('nodeExecuteAfter', [
1216
1282
  executionNode.name,
1217
1283
  taskData,
1218
1284
  this.runExecutionData,
1219
1285
  ]);
1220
- this.runExecutionData.executionData.nodeExecutionStack.unshift(executionData);
1286
+ this.pushExecutionStack(executionData);
1221
1287
  break;
1222
1288
  }
1223
1289
  if (this.runExecutionData?.startData?.destinationNode?.nodeName === executionNode.name) {