diff --git a/packages/cli/src/errors/aborted-execution-retry.error.ts b/packages/cli/src/errors/aborted-execution-retry.error.ts new file mode 100644 index 000000000..20d8b57e1 --- /dev/null +++ b/packages/cli/src/errors/aborted-execution-retry.error.ts @@ -0,0 +1,9 @@ +import { ApplicationError } from 'n8n-workflow'; + +export class AbortedExecutionRetryError extends ApplicationError { + constructor() { + super('The execution was aborted before starting, so it cannot be retried', { + level: 'warning', + }); + } +} diff --git a/packages/cli/src/executions/execution.service.ts b/packages/cli/src/executions/execution.service.ts index 2487dac2b..c245e2124 100644 --- a/packages/cli/src/executions/execution.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -36,6 +36,7 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error'; import config from '@/config'; import { WaitTracker } from '@/WaitTracker'; import type { ExecutionEntity } from '@/databases/entities/ExecutionEntity'; +import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; export const schemaGetExecutionsQueryFilter = { $id: '/IGetExecutionsQueryFilter', @@ -129,6 +130,8 @@ export class ExecutionService { throw new NotFoundError(`The execution with the ID "${executionId}" does not exist.`); } + if (!execution.data.executionData) throw new AbortedExecutionRetryError(); + if (execution.finished) { throw new ApplicationError('The execution succeeded, so it cannot be retried.'); } diff --git a/packages/cli/test/unit/services/execution.service.test.ts b/packages/cli/test/unit/services/execution.service.test.ts new file mode 100644 index 000000000..e607fe0b6 --- /dev/null +++ b/packages/cli/test/unit/services/execution.service.test.ts @@ -0,0 +1,30 @@ +import type { IExecutionResponse } from '@/Interfaces'; +import type { ExecutionRepository } from '@/databases/repositories/execution.repository'; +import { AbortedExecutionRetryError } from '@/errors/aborted-execution-retry.error'; +import { ExecutionService } from '@/executions/execution.service'; +import type { ExecutionRequest } from '@/executions/execution.types'; +import { mock } from 'jest-mock-extended'; + +describe('ExecutionService', () => { + const executionRepository = mock(); + const executionService = new ExecutionService( + mock(), + mock(), + mock(), + executionRepository, + mock(), + mock(), + mock(), + mock(), + ); + + it('should error on retrying an aborted execution', async () => { + const abortedExecutionData = mock({ data: { executionData: undefined } }); + executionRepository.findWithUnflattenedData.mockResolvedValue(abortedExecutionData); + const req = mock(); + + const retry = executionService.retry(req, []); + + await expect(retry).rejects.toThrow(AbortedExecutionRetryError); + }); +}); diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 6ebd2b208..023c8398d 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1218,7 +1218,7 @@ "nodeView.runButtonText.executeWorkflow": "Test workflow", "nodeView.runButtonText.executingWorkflow": "Executing workflow", "nodeView.runButtonText.waitingForTriggerEvent": "Waiting for trigger event", - "nodeView.showError.workflowError": "Workflow execution finished with an error", + "nodeView.showError.workflowError": "Workflow execution had an error", "nodeView.showError.getWorkflowDataFromUrl.title": "Problem loading workflow", "nodeView.showError.importWorkflowData.title": "Problem importing workflow", "nodeView.showError.mounted1.message": "There was a problem loading init data",