From f598b3bf00a37fc9c89639b438bc8f25438fbae0 Mon Sep 17 00:00:00 2001 From: RomanDavydchuk Date: Fri, 20 Jun 2025 12:58:52 +0300 Subject: [PATCH] fix(core): Incorrect data returned by the node when using retry on error + continue on error (#16516) --- .../retry_and_continue_on_error.json | 752 ++++++++++++++++++ .../src/execution-engine/workflow-execute.ts | 4 +- 2 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/execution-engine/__tests__/workflows/retry_and_continue_on_error.json diff --git a/packages/core/src/execution-engine/__tests__/workflows/retry_and_continue_on_error.json b/packages/core/src/execution-engine/__tests__/workflows/retry_and_continue_on_error.json new file mode 100644 index 0000000000..2aaf685d10 --- /dev/null +++ b/packages/core/src/execution-engine/__tests__/workflows/retry_and_continue_on_error.json @@ -0,0 +1,752 @@ +{ + "name": "Retry and Continue On Fail", + "nodes": [ + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [-1340, 1160], + "id": "b406074d-3ceb-4e2d-9b80-2edfc28e474f", + "name": "When clicking ‘Execute workflow’" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, -240], + "id": "1efde9e7-42ef-4768-b839-bf12db612238", + "name": "Reset Count 1" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 1) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, -240], + "id": "23496ea1-efc9-4ed0-b1b2-b6cce126e85f", + "name": "Retry + Continue 1", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 2) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, -40], + "id": "4e26b167-7c8d-4429-b512-5f740d999dd5", + "name": "Retry + Continue 2", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, -40], + "id": "00349f2d-158f-428e-9d2a-120abc84b054", + "name": "Reset Count 2" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 160], + "id": "fe52c115-b4a0-4cd7-a674-3e50c67b7a8b", + "name": "Reset Count 3" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 3) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 160], + "id": "481422a1-2f5a-4779-98d5-a79b797a49c7", + "name": "Retry + Continue 3", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 360], + "id": "85835f42-5168-42c7-88c4-222dc961e0f7", + "name": "Reset Count 4" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 4) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 360], + "id": "2e7e4fe3-0b86-4aba-9f0d-d5c27e50d9ff", + "name": "Retry + Continue 4", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 560], + "id": "325c34f5-57b9-41be-85f8-2baf9e6a635a", + "name": "Reset Count 5" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 1) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 560], + "id": "7cfe8e6d-d10e-469e-9437-8942e67a9f8e", + "name": "Retry + Continue 5", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 2) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 760], + "id": "266e33fa-43cd-47fb-9ab6-4b97cb9c4e90", + "name": "Retry + Continue 6", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 760], + "id": "cf07ee5d-4324-490f-a6f8-bdce7bff5e61", + "name": "Reset Count 6" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 960], + "id": "048c2afe-472d-4a95-9960-340577fa3873", + "name": "Reset Count 7" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 3) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 960], + "id": "ea5e6a55-215b-44c5-9b8d-8fab9f37be8a", + "name": "Retry + Continue 7", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 1160], + "id": "a5d9252a-8859-4faf-b237-2cbdef8a90d9", + "name": "Reset Count 8" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 4) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 1160], + "id": "500109e7-b67f-4b84-a894-85967f3ef05b", + "name": "Retry + Continue 8", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 1360], + "id": "8f35bd4a-acff-4349-a94d-fe5c3c09ca2e", + "name": "Reset Count 9" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 1) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 1360], + "id": "b56d9fdf-6069-4a05-a160-013675bcbe26", + "name": "Continue 1", + "retryOnFail": false, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 1560], + "id": "acb0baee-416b-4688-8475-47a80a02c2e3", + "name": "Reset Count 10" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 2) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 1560], + "id": "102f6929-9979-4aea-ba80-4f723a33e93b", + "name": "Continue 2", + "retryOnFail": false, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 1760], + "id": "92a72005-4d11-46ca-a750-3245b7857af5", + "name": "Reset Count 11" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 1960], + "id": "400bc501-d027-4f9d-b5f1-e51eec20258e", + "name": "Reset Count 12" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 2) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 1960], + "id": "92c4fc2a-5fff-4de9-afd6-d6fd9558718a", + "name": "Continue 4", + "retryOnFail": false, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 1) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 1760], + "id": "5bad0503-34e6-44fd-b661-6edfa2e1140f", + "name": "Continue 3", + "retryOnFail": false, + "maxTries": 3, + "waitBetweenTries": 10, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 2160], + "id": "20544912-6307-4de6-956b-c46ad0d8284b", + "name": "Reset Count 13" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 2360], + "id": "99cbdd44-78e5-4339-9597-bc14a5a93706", + "name": "Reset Count 14" + }, + { + "parameters": { + "jsCode": "$execution.customData.set('count', '1')\nreturn [{}]" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-1120, 2560], + "id": "4a653394-dca7-4777-8257-4c397247d2f9", + "name": "Reset Count 15" + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 1) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 2160], + "id": "a753e381-1d69-46fd-8bd7-0899a3b6c12e", + "name": "Retry 1", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10 + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 2) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 2360], + "id": "556ee460-b807-4e82-95ea-135903ed1457", + "name": "Retry 2", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10 + }, + { + "parameters": { + "jsCode": "const count = +$execution.customData.get('count')\n$execution.customData.set('count', `${count + 1}`)\n\nif (count === 3) {\n return { success: true, count }\n}\n\nthrow new Error('simulated error, count = ' + count)" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [-900, 2560], + "id": "b143b146-61c7-4762-ada5-6b5f2681cd58", + "name": "Retry 3", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 10 + } + ], + "pinData": { + "Retry + Continue 2": [ + { + "json": { + "success": true, + "count": 2 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 1": [ + { + "json": { + "success": true, + "count": 1 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 5": [ + { + "json": { + "success": true, + "count": 1 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 6": [ + { + "json": { + "success": true, + "count": 2 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 8": [], + "Continue 1": [ + { + "json": { + "success": true, + "count": 1 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Continue 2": [ + { + "json": { + "error": "simulated error, count = 1 [line 8]" + }, + "pairedItem": { + "item": 0 + } + } + ], + "Continue 4": [], + "Continue 3": [ + { + "json": { + "success": true, + "count": 1 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry 2": [ + { + "json": { + "success": true, + "count": 2 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 4": [ + { + "json": { + "error": "simulated error, count = 3 [line 8]" + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry + Continue 3": [ + { + "json": { + "success": true, + "count": 3 + }, + "pairedItem": { + "item": 0 + } + } + ], + "Retry 3": [ + { + "json": { + "success": true, + "count": 3 + }, + "pairedItem": { + "item": 0 + } + } + ] + }, + "connections": { + "When clicking ‘Execute workflow’": { + "main": [ + [ + { + "node": "Reset Count 1", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 2", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 3", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 4", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 5", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 6", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 7", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 8", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 9", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 10", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 11", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 12", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 13", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 14", + "type": "main", + "index": 0 + }, + { + "node": "Reset Count 15", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 1": { + "main": [ + [ + { + "node": "Retry + Continue 1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 2": { + "main": [ + [ + { + "node": "Retry + Continue 2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 3": { + "main": [ + [ + { + "node": "Retry + Continue 3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 4": { + "main": [ + [ + { + "node": "Retry + Continue 4", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 5": { + "main": [ + [ + { + "node": "Retry + Continue 5", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 6": { + "main": [ + [ + { + "node": "Retry + Continue 6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 7": { + "main": [ + [ + { + "node": "Retry + Continue 7", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 8": { + "main": [ + [ + { + "node": "Retry + Continue 8", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 9": { + "main": [ + [ + { + "node": "Continue 1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 10": { + "main": [ + [ + { + "node": "Continue 2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 11": { + "main": [ + [ + { + "node": "Continue 3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 12": { + "main": [ + [ + { + "node": "Continue 4", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 13": { + "main": [ + [ + { + "node": "Retry 1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 14": { + "main": [ + [ + { + "node": "Retry 2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Reset Count 15": { + "main": [ + [ + { + "node": "Retry 3", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "5fa8975e-6199-4d5f-b923-333b1655b370", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e" + }, + "id": "7nMyetIM9ubB2Wwu", + "tags": [] +} diff --git a/packages/core/src/execution-engine/workflow-execute.ts b/packages/core/src/execution-engine/workflow-execute.ts index ab8231a226..8612e31a68 100644 --- a/packages/core/src/execution-engine/workflow-execute.ts +++ b/packages/core/src/execution-engine/workflow-execute.ts @@ -1547,7 +1547,7 @@ export class WorkflowExecute { nodeSuccessData = runNodeData.data; - const didContinueOnFail = nodeSuccessData?.[0]?.[0]?.json?.error !== undefined; + let didContinueOnFail = nodeSuccessData?.[0]?.[0]?.json?.error !== undefined; while (didContinueOnFail && tryIndex !== maxTries - 1) { await sleep(waitBetweenTries); @@ -1562,6 +1562,8 @@ export class WorkflowExecute { this.abortController.signal, ); + nodeSuccessData = runNodeData.data; + didContinueOnFail = nodeSuccessData?.[0]?.[0]?.json?.error !== undefined; tryIndex++; }