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.
- package/dist/binary-data/blob.manager.d.ts +1 -0
- package/dist/binary-data/blob.manager.js +5 -0
- package/dist/binary-data/blob.manager.js.map +1 -1
- package/dist/binary-data/index.d.ts +1 -1
- package/dist/binary-data/index.js +2 -1
- package/dist/binary-data/index.js.map +1 -1
- package/dist/build.tsbuildinfo +1 -1
- package/dist/encryption/cipher.js.map +1 -1
- package/dist/execution-engine/node-execution-context/hook-context.js +2 -1
- package/dist/execution-engine/node-execution-context/hook-context.js.map +1 -1
- package/dist/execution-engine/node-execution-context/load-options-context.js +2 -1
- package/dist/execution-engine/node-execution-context/load-options-context.js.map +1 -1
- package/dist/execution-engine/node-execution-context/poll-context.d.ts +2 -1
- package/dist/execution-engine/node-execution-context/poll-context.js +5 -2
- package/dist/execution-engine/node-execution-context/poll-context.js.map +1 -1
- package/dist/execution-engine/node-execution-context/supply-data-context.d.ts +1 -1
- package/dist/execution-engine/node-execution-context/supply-data-context.js +2 -0
- package/dist/execution-engine/node-execution-context/supply-data-context.js.map +1 -1
- package/dist/execution-engine/node-execution-context/trigger-context.js +2 -1
- package/dist/execution-engine/node-execution-context/trigger-context.js.map +1 -1
- package/dist/execution-engine/node-execution-context/utils/get-input-connection-data.js +3 -2
- package/dist/execution-engine/node-execution-context/utils/get-input-connection-data.js.map +1 -1
- package/dist/execution-engine/node-execution-context/utils/request-helpers/authentication.js +9 -9
- package/dist/execution-engine/node-execution-context/utils/request-helpers/authentication.js.map +1 -1
- package/dist/execution-engine/node-execution-context/utils/request-helpers/factory.js +2 -1
- package/dist/execution-engine/node-execution-context/utils/request-helpers/factory.js.map +1 -1
- package/dist/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.js +1 -3
- package/dist/execution-engine/node-execution-context/utils/request-helpers/legacy-request-adapter.js.map +1 -1
- package/dist/execution-engine/node-execution-context/utils/request-helpers/oauth.js +44 -3
- package/dist/execution-engine/node-execution-context/utils/request-helpers/oauth.js.map +1 -1
- package/dist/execution-engine/node-execution-context/webhook-context.d.ts +1 -0
- package/dist/execution-engine/node-execution-context/webhook-context.js +5 -1
- package/dist/execution-engine/node-execution-context/webhook-context.js.map +1 -1
- package/dist/execution-engine/workflow-execute.d.ts +25 -0
- package/dist/execution-engine/workflow-execute.js +375 -309
- package/dist/execution-engine/workflow-execute.js.map +1 -1
- 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('
|
|
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
|
-
(
|
|
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
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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.
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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.
|
|
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
|
|
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
|
|
972
|
-
if (
|
|
973
|
-
|
|
974
|
-
nodeSuccessData = [nodePinData];
|
|
1203
|
+
const pinnedOutput = this.getPinnedOutput(executionNode);
|
|
1204
|
+
if (pinnedOutput) {
|
|
1205
|
+
nodeSuccessData = pinnedOutput;
|
|
975
1206
|
}
|
|
976
1207
|
else {
|
|
977
|
-
|
|
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
|
-
|
|
1015
|
-
nodeSuccessData =
|
|
1016
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1199
|
-
|
|
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.
|
|
1286
|
+
this.pushExecutionStack(executionData);
|
|
1221
1287
|
break;
|
|
1222
1288
|
}
|
|
1223
1289
|
if (this.runExecutionData?.startData?.destinationNode?.nodeName === executionNode.name) {
|