From a2f80d882bd9f7f56c035fdba2d127fc381f2b72 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Thu, 5 Mar 2026 11:24:36 +0100 Subject: [PATCH] Stop catching all workflow errors (#18392) Steps now throw WorkflowStepExecutorException. Then workflow executor decides if error should be catch or not. Since tools are not only used in workflow and these do not throw, we may still miss errors here. Workflow jobs now only catch errors to end the workflow run and throw. --- .../ai-agent/ai-agent.workflow-action.ts | 97 ++++++++----------- .../code/code.workflow-action.ts | 26 +++-- .../delay/jobs/resume-delayed-workflow.job.ts | 2 + .../logic-function.workflow-action.ts | 64 ++++++------ .../create-record.workflow-action.ts | 9 +- .../delete-record.workflow-action.ts | 13 +-- .../find-records.workflow-action.ts | 9 +- .../update-record.workflow-action.ts | 17 +--- .../upsert-record.workflow-action.ts | 13 +-- ...orkflow-executor.workspace-service.spec.ts | 9 ++ .../workflow-executor.workspace-service.ts | 18 ++++ .../workflow-runner/jobs/run-workflow.job.ts | 2 + 12 files changed, 125 insertions(+), 154 deletions(-) diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts index aec6c83273..c5b4dec11c 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/ai-agent/ai-agent.workflow-action.ts @@ -7,10 +7,6 @@ import { type Repository } from 'typeorm'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; import { AgentAsyncExecutorService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-async-executor.service'; -import { - AgentException, - AgentExceptionCode, -} from 'src/engine/metadata-modules/ai/ai-agent/agent.exception'; import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; import { AIBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service'; import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; @@ -56,60 +52,47 @@ export class AiAgentWorkflowAction implements WorkflowAction { const { agentId, prompt } = step.settings.input; const workspaceId = context.workspaceId as string; - try { - let agent: AgentEntity | null = null; + let agent: AgentEntity | null = null; - if (agentId) { - agent = await this.agentRepository.findOne({ - where: { - id: agentId, - workspaceId, - }, - }); - } - - if (agentId && !agent) { - throw new AgentException( - `Agent with id ${agentId} not found`, - AgentExceptionCode.AGENT_NOT_FOUND, - ); - } - - const executionContext = - await this.workflowExecutionContextService.getExecutionContext(runInfo); - - const { result, usage, cacheCreationTokens } = - await this.aiAgentExecutionService.executeAgent({ - agent, - userPrompt: resolveInput(prompt, context) as string, - actorContext: executionContext.isActingOnBehalfOfUser - ? executionContext.initiator - : undefined, - rolePermissionConfig: executionContext.rolePermissionConfig, - authContext: executionContext.authContext, - }); - - await this.aiBillingService.calculateAndBillUsage( - agent?.modelId ?? DEFAULT_SMART_MODEL, - { usage, cacheCreationTokens }, - workspaceId, - agent?.id || null, - ); - - return { - result, - }; - } catch (error) { - if (error instanceof AgentException) { - return { - error: `${error.message} (${error.code})`, - }; - } - - return { - error: - error instanceof Error ? error.message : 'AI Agent execution failed', - }; + if (agentId) { + agent = await this.agentRepository.findOne({ + where: { + id: agentId, + workspaceId, + }, + }); } + + if (agentId && !agent) { + throw new WorkflowStepExecutorException( + `Agent with id ${agentId} not found`, + WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT, + ); + } + + const executionContext = + await this.workflowExecutionContextService.getExecutionContext(runInfo); + + const { result, usage, cacheCreationTokens } = + await this.aiAgentExecutionService.executeAgent({ + agent, + userPrompt: resolveInput(prompt, context) as string, + actorContext: executionContext.isActingOnBehalfOfUser + ? executionContext.initiator + : undefined, + rolePermissionConfig: executionContext.rolePermissionConfig, + authContext: executionContext.authContext, + }); + + await this.aiBillingService.calculateAndBillUsage( + agent?.modelId ?? DEFAULT_SMART_MODEL, + { usage, cacheCreationTokens }, + workspaceId, + agent?.id || null, + ); + + return { + result, + }; } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action.ts index c1d8bf3907..b5189c39ca 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action.ts @@ -4,6 +4,7 @@ import { resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; +import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service'; import { WorkflowStepExecutorException, WorkflowStepExecutorExceptionCode, @@ -13,7 +14,6 @@ import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executo import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util'; import { isWorkflowCodeAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/guards/is-workflow-code-action.guard'; import { type WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type'; -import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service'; @Injectable() export class CodeWorkflowAction implements WorkflowAction { @@ -44,22 +44,18 @@ export class CodeWorkflowAction implements WorkflowAction { context, ) as WorkflowCodeActionInput; - try { - const { workspaceId } = runInfo; + const { workspaceId } = runInfo; - const result = await this.logicFunctionExecutorService.execute({ - logicFunctionId: workflowActionInput.logicFunctionId, - workspaceId, - payload: workflowActionInput.logicFunctionInput, - }); + const result = await this.logicFunctionExecutorService.execute({ + logicFunctionId: workflowActionInput.logicFunctionId, + workspaceId, + payload: workflowActionInput.logicFunctionInput, + }); - if (result.error) { - return { error: result.error.errorMessage }; - } - - return { result: result.data || {} }; - } catch (error) { - return { error: error.message }; + if (result.error) { + return { error: result.error.errorMessage }; } + + return { result: result.data || {} }; } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts index 2f3c6650ca..2b17874e96 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts @@ -103,6 +103,8 @@ export class ResumeDelayedWorkflowJob { ? error.message : `Error during delay resume: ${String(error)}`, }); + + throw error; } }, authContext); } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action.ts index 91b7a7c050..99a0c7104e 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action.ts @@ -4,6 +4,7 @@ import { resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; +import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { @@ -15,7 +16,6 @@ import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executo import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util'; import { isWorkflowLogicFunctionAction } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/guards/is-workflow-logic-function-action.guard'; import { WorkflowLogicFunctionActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/types/workflow-logic-function-action-input.type'; -import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service'; @Injectable() export class LogicFunctionWorkflowAction implements WorkflowAction { @@ -47,42 +47,38 @@ export class LogicFunctionWorkflowAction implements WorkflowAction { context, ) as WorkflowLogicFunctionActionInput; - try { - const { workspaceId } = runInfo; + const { workspaceId } = runInfo; - const { flatLogicFunctionMaps } = - await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: ['flatLogicFunctionMaps'], - }, - ); + const { flatLogicFunctionMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatLogicFunctionMaps'], + }, + ); - const logicFunction = findFlatEntityByIdInFlatEntityMaps({ - flatEntityId: workflowActionInput.logicFunctionId, - flatEntityMaps: flatLogicFunctionMaps, - }); + const logicFunction = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: workflowActionInput.logicFunctionId, + flatEntityMaps: flatLogicFunctionMaps, + }); - if (!logicFunction) { - throw new WorkflowStepExecutorException( - `Logic function with id ${workflowActionInput.logicFunctionId} not found`, - WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE, - ); - } - - const result = await this.logicFunctionExecutorService.execute({ - logicFunctionId: workflowActionInput.logicFunctionId, - workspaceId, - payload: workflowActionInput.logicFunctionInput, - }); - - if (result.error) { - return { error: result.error.errorMessage }; - } - - return { result: result.data || {} }; - } catch (error) { - return { error: error.message }; + if (!logicFunction) { + throw new WorkflowStepExecutorException( + `Logic function with id ${workflowActionInput.logicFunctionId} not found`, + WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE, + ); } + + const result = await this.logicFunctionExecutorService.execute({ + logicFunctionId: workflowActionInput.logicFunctionId, + workspaceId, + payload: workflowActionInput.logicFunctionInput, + }); + + if (result.error) { + return { error: result.error.errorMessage }; + } + + return { result: result.data || {} }; } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts index 1c7585d97c..d55a59fc3b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action.ts @@ -4,10 +4,6 @@ import { resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; -import { - RecordCrudException, - RecordCrudExceptionCode, -} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { CreateRecordService } from 'src/engine/core-modules/record-crud/services/create-record.service'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-executor/services/workflow-execution-context.service'; @@ -82,10 +78,7 @@ export class CreateRecordWorkflowAction implements WorkflowAction { }); if (!toolOutput.success) { - throw new RecordCrudException( - toolOutput.error || toolOutput.message, - RecordCrudExceptionCode.RECORD_CREATION_FAILED, - ); + return { error: toolOutput.error || toolOutput.message }; } return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts index a8e858d345..a94b7aefda 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action.ts @@ -4,10 +4,6 @@ import { isDefined, isValidUuid, resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; -import { - RecordCrudException, - RecordCrudExceptionCode, -} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { DeleteRecordService } from 'src/engine/core-modules/record-crud/services/delete-record.service'; import { WorkflowStepExecutorException, @@ -55,9 +51,9 @@ export class DeleteRecordWorkflowAction implements WorkflowAction { !isValidUuid(workflowActionInput.objectRecordId) || !isDefined(workflowActionInput.objectName) ) { - throw new RecordCrudException( + throw new WorkflowStepExecutorException( 'Failed to delete: Object record ID and name are required', - RecordCrudExceptionCode.INVALID_REQUEST, + WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT, ); } @@ -73,10 +69,7 @@ export class DeleteRecordWorkflowAction implements WorkflowAction { }); if (!toolOutput.success) { - throw new RecordCrudException( - toolOutput.error || toolOutput.message, - RecordCrudExceptionCode.RECORD_DELETION_FAILED, - ); + return { error: toolOutput.error || toolOutput.message }; } return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts index 4b2f0586f6..96a12d05b4 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts @@ -12,10 +12,6 @@ import { import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; -import { - RecordCrudException, - RecordCrudExceptionCode, -} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; @@ -121,10 +117,7 @@ export class FindRecordsWorkflowAction implements WorkflowAction { }); if (!toolOutput.success) { - throw new RecordCrudException( - toolOutput.error || toolOutput.message, - RecordCrudExceptionCode.QUERY_FAILED, - ); + return { error: toolOutput.error || toolOutput.message }; } const records = toolOutput.result?.records ?? []; diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts index 9e5772c498..2ce5ef240c 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action.ts @@ -4,10 +4,6 @@ import { isDefined, isValidUuid, resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; -import { - RecordCrudException, - RecordCrudExceptionCode, -} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { @@ -79,9 +75,9 @@ export class UpdateRecordWorkflowAction implements WorkflowAction { !isValidUuid(workflowActionInput.objectRecordId) || !isDefined(workflowActionInput.objectName) ) { - throw new RecordCrudException( + throw new WorkflowStepExecutorException( 'Failed to update: Object record ID and name are required', - RecordCrudExceptionCode.INVALID_REQUEST, + WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT, ); } @@ -96,9 +92,9 @@ export class UpdateRecordWorkflowAction implements WorkflowAction { ); if (filteredFieldsToUpdate?.length === 0) { - throw new RecordCrudException( + throw new WorkflowStepExecutorException( 'Failed to update: No fields to update', - RecordCrudExceptionCode.INVALID_REQUEST, + WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT, ); } @@ -118,10 +114,7 @@ export class UpdateRecordWorkflowAction implements WorkflowAction { }); if (!toolOutput.success) { - throw new RecordCrudException( - toolOutput.error || toolOutput.message, - RecordCrudExceptionCode.RECORD_UPDATE_FAILED, - ); + return { error: toolOutput.error || toolOutput.message }; } return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts index 4b1d36bf56..a28108b5c5 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/upsert-record.workflow-action.ts @@ -4,10 +4,6 @@ import { isDefined, resolveInput } from 'twenty-shared/utils'; import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; -import { - RecordCrudException, - RecordCrudExceptionCode, -} from 'src/engine/core-modules/record-crud/exceptions/record-crud.exception'; import { UpsertRecordService } from 'src/engine/core-modules/record-crud/services/upsert-record.service'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { @@ -74,9 +70,9 @@ export class UpsertRecordWorkflowAction implements WorkflowAction { ) as WorkflowUpsertRecordActionInput; if (!isDefined(workflowActionInput.objectName)) { - throw new RecordCrudException( + throw new WorkflowStepExecutorException( 'Failed to upsert: Object name is required', - RecordCrudExceptionCode.INVALID_REQUEST, + WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT, ); } @@ -97,10 +93,7 @@ export class UpsertRecordWorkflowAction implements WorkflowAction { }); if (!toolOutput.success) { - throw new RecordCrudException( - toolOutput.error || toolOutput.message, - RecordCrudExceptionCode.RECORD_UPSERT_FAILED, - ); + return { error: toolOutput.error || toolOutput.message }; } return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/__tests__/workflow-executor.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/__tests__/workflow-executor.workspace-service.spec.ts index e93a0f91c6..0bc1e77c14 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/__tests__/workflow-executor.workspace-service.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/__tests__/workflow-executor.workspace-service.spec.ts @@ -14,6 +14,7 @@ import { type WorkflowAction, WorkflowActionType, } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service'; import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service'; import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; @@ -56,6 +57,10 @@ describe('WorkflowExecutorWorkspaceService', () => { canBillMeteredProduct: jest.fn().mockReturnValue(true), }; + const mockExceptionHandlerService = { + captureExceptions: jest.fn(), + }; + const mockMessageQueueService = { add: jest.fn(), }; @@ -84,6 +89,10 @@ describe('WorkflowExecutorWorkspaceService', () => { provide: BillingService, useValue: mockBillingService, }, + { + provide: ExceptionHandlerService, + useValue: mockExceptionHandlerService, + }, { provide: `MESSAGE_QUEUE_${MessageQueue.workflowQueue}`, useValue: mockMessageQueueService, diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts index 6d94d12d61..cafc46064b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts @@ -15,12 +15,17 @@ import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/bil import { BillingProductKey } from 'src/engine/core-modules/billing/enums/billing-product-key.enum'; import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; import { type BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type'; +import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { WorkflowRunStatus } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { workflowHasRunningSteps } from 'src/modules/workflow/common/utils/workflow-has-running-steps.util'; +import { + WorkflowStepExecutorException, + WorkflowStepExecutorExceptionCode, +} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception'; import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory'; import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type'; import { @@ -51,6 +56,7 @@ export class WorkflowExecutorWorkspaceService { private readonly workspaceEventEmitter: WorkspaceEventEmitter, private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService, private readonly billingService: BillingService, + private readonly exceptionHandlerService: ExceptionHandlerService, @InjectMessageQueue(MessageQueue.workflowQueue) private readonly messageQueueService: MessageQueueService, ) {} @@ -484,6 +490,18 @@ export class WorkflowExecutorWorkspaceService { }, }); } catch (error) { + const isUserError = + error instanceof WorkflowStepExecutorException && + (error.code === WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE || + error.code === WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT || + error.code === WorkflowStepExecutorExceptionCode.STEP_NOT_FOUND); + + if (!isUserError) { + this.exceptionHandlerService.captureExceptions([error], { + workspace: { id: workspaceId }, + }); + } + return { error: error.message ?? 'Execution result error, no data or error', }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts index a91d83ad37..9449065da8 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts @@ -62,6 +62,8 @@ export class RunWorkflowJob { status: WorkflowRunStatus.FAILED, error: error.message, }); + + throw error; } }, authContext); }