diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index ad4bccd89a390..0d5140d2ac34c 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -1,6 +1,7 @@ import type { FindOptionsWhere } from 'typeorm'; import { In } from 'typeorm'; import { Container } from 'typedi'; + import type { IDataObject, IExecuteData, @@ -11,17 +12,20 @@ import type { ITaskData, NodeApiError, WorkflowExecuteMode, + WorkflowOperationError, } from 'n8n-workflow'; import { ErrorReporterProxy as ErrorReporter, LoggerProxy as Logger, NodeOperationError, + SubworkflowOperationError, Workflow, } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import * as Db from '@/Db'; import type { ICredentialsDb, + IExecutionDb, IWorkflowErrorData, IWorkflowExecutionDataProcess, } from '@/Interfaces'; @@ -39,11 +43,57 @@ import { UserService } from './user/user.service'; import type { SharedWorkflow } from '@db/entities/SharedWorkflow'; import type { RoleNames } from '@db/entities/Role'; import { RoleService } from './services/role.service'; -import { RoleRepository } from './databases/repositories'; +import { ExecutionRepository, RoleRepository } from './databases/repositories'; import { VariablesService } from './environments/variables/variables.service'; const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); +export function generateFailedExecutionFromError( + mode: WorkflowExecuteMode, + error: NodeApiError | NodeOperationError | WorkflowOperationError, + node: INode, +): IRun { + return { + data: { + startData: { + destinationNode: node.name, + runNodeFilter: [node.name], + }, + resultData: { + error, + runData: { + [node.name]: [ + { + startTime: 0, + executionTime: 0, + error, + source: [], + }, + ], + }, + lastNodeExecuted: node.name, + }, + executionData: { + contextData: {}, + nodeExecutionStack: [ + { + node, + data: {}, + source: null, + }, + ], + waitingExecution: {}, + waitingExecutionSource: {}, + }, + }, + finished: false, + mode, + startedAt: new Date(), + stoppedAt: new Date(), + status: 'failed', + }; +} + /** * Returns the data of the last executed node * @@ -127,6 +177,34 @@ export async function executeErrorWorkflow( workflowErrorData.workflow.id, ); } catch (error) { + const initialNode = workflowInstance.getStartNode(); + if (initialNode) { + const errorWorkflowPermissionError = new SubworkflowOperationError( + `Another workflow: (ID ${workflowErrorData.workflow.id}) tried to invoke this workflow to handle errors.`, + "Unfortunately current permissions do not allow this. Please check that this workflow's settings allow it to be called by others", + ); + + // Create a fake execution and save it to DB. + const fakeExecution = generateFailedExecutionFromError( + 'error', + errorWorkflowPermissionError, + initialNode, + ); + + const fullExecutionData: IExecutionDb = { + data: fakeExecution.data, + mode: fakeExecution.mode, + finished: false, + startedAt: new Date(), + stoppedAt: new Date(), + workflowData, + waitTill: null, + status: fakeExecution.status, + workflowId: workflowData.id, + }; + + await Container.get(ExecutionRepository).createNewExecution(fullExecutionData); + } Logger.info('Error workflow execution blocked due to subworkflow settings', { erroredWorkflowId: workflowErrorData.workflow.id, errorWorkflowId: workflowId, @@ -445,52 +523,6 @@ export async function isBelowOnboardingThreshold(user: User): Promise { return belowThreshold; } -export function generateFailedExecutionFromError( - mode: WorkflowExecuteMode, - error: NodeApiError | NodeOperationError, - node: INode, -): IRun { - return { - data: { - startData: { - destinationNode: node.name, - runNodeFilter: [node.name], - }, - resultData: { - error, - runData: { - [node.name]: [ - { - startTime: 0, - executionTime: 0, - error, - source: [], - }, - ], - }, - lastNodeExecuted: node.name, - }, - executionData: { - contextData: {}, - nodeExecutionStack: [ - { - node, - data: {}, - source: null, - }, - ], - waitingExecution: {}, - waitingExecutionSource: {}, - }, - }, - finished: false, - mode, - startedAt: new Date(), - stoppedAt: new Date(), - status: 'failed', - }; -} - /** Get all nodes in a workflow where the node credential is not accessible to the user. */ export function getNodesWithInaccessibleCreds(workflow: WorkflowEntity, userCredIds: string[]) { if (!workflow.nodes) { diff --git a/packages/editor-ui/src/mixins/executionsHelpers.ts b/packages/editor-ui/src/mixins/executionsHelpers.ts index c5e417a8ef9e5..29a6c729485dc 100644 --- a/packages/editor-ui/src/mixins/executionsHelpers.ts +++ b/packages/editor-ui/src/mixins/executionsHelpers.ts @@ -41,19 +41,15 @@ export const executionHelpers = defineComponent({ runningTime: '', }; - if (execution.status === 'waiting' || execution.waitTill) { + if (execution.status === 'waiting') { status.name = 'waiting'; status.label = this.$locale.baseText('executionsList.waiting'); } else if (execution.status === 'canceled') { status.label = this.$locale.baseText('executionsList.canceled'); - } else if ( - execution.status === 'running' || - execution.status === 'new' || - execution.stoppedAt === undefined - ) { + } else if (execution.status === 'running' || execution.status === 'new') { status.name = 'running'; status.label = this.$locale.baseText('executionsList.running'); - } else if (execution.status === 'success' || execution.finished) { + } else if (execution.status === 'success') { status.name = 'success'; status.label = this.$locale.baseText('executionsList.succeeded'); } else if (execution.status === 'failed' || execution.status === 'crashed') { diff --git a/packages/workflow/src/WorkflowErrors.ts b/packages/workflow/src/WorkflowErrors.ts index c161d4475fad5..79ab34f7159d0 100644 --- a/packages/workflow/src/WorkflowErrors.ts +++ b/packages/workflow/src/WorkflowErrors.ts @@ -1,9 +1,10 @@ import type { INode } from './Interfaces'; +import { ExecutionBaseError } from './NodeErrors'; /** * Class for instantiating an operational error, e.g. a timeout error. */ -export class WorkflowOperationError extends Error { +export class WorkflowOperationError extends ExecutionBaseError { node: INode | undefined; timestamp: number; @@ -13,7 +14,7 @@ export class WorkflowOperationError extends Error { description: string | undefined; constructor(message: string, node?: INode) { - super(message); + super(message, { cause: undefined }); this.name = this.constructor.name; this.node = node; this.timestamp = Date.now();