From 8205c97b5b2cea530aa4c382b57e5473c2695adb Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 16 Jun 2026 12:45:54 +0200 Subject: [PATCH] fix(route-trigger): return 422 instead of 500 for logic function execution errors (#21656) ## What Return HTTP 422 instead of 500 for `LOGIC_FUNCTION_EXECUTION_ERROR` in the route trigger exception filter. ## Why When a logic function's user code fails (e.g. an HTTP call inside the function returns a 502 from an upstream service), the exception was mapped to HTTP 500. This caused two problems: - **Sentry noise**: `shouldCaptureException` captures all 5xx responses, so every user-code failure was reported as a platform error. This generated ~56k Sentry events over 2 months for a single workspace's logic function hitting a transient upstream 502. - **Webhook retry loops**: Webhook senders like GitHub auto-retry on 5xx responses, amplifying the event count. `LOGIC_FUNCTION_EXECUTION_ERROR` is a user-code error, not a platform error. A 422 (Unprocessable Entity) correctly signals that the request could not be processed due to the logic function's own failure, without triggering Sentry capture or webhook retries. Review in cubic --- .../exception-handler/http-exception-handler.service.ts | 4 ++++ .../exceptions/route-trigger-rest-api-exception-filter.ts | 3 +++ .../src/engine/utils/global-exception-handler.util.ts | 7 ++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/twenty-server/src/engine/core-modules/exception-handler/http-exception-handler.service.ts b/packages/twenty-server/src/engine/core-modules/exception-handler/http-exception-handler.service.ts index e3034fc3eb..9b7e5715b3 100644 --- a/packages/twenty-server/src/engine/core-modules/exception-handler/http-exception-handler.service.ts +++ b/packages/twenty-server/src/engine/core-modules/exception-handler/http-exception-handler.service.ts @@ -73,6 +73,9 @@ export class HttpExceptionHandlerService { errorCode?: number, user?: ExceptionHandlerUser, workspace?: ExceptionHandlerWorkspace, + { + shouldBeCapturedBySentry = true, + }: { shouldBeCapturedBySentry?: boolean } = {}, ): Response | undefined => { const params = this.request?.params; @@ -116,6 +119,7 @@ export class HttpExceptionHandlerService { user, workspace, statusCode, + shouldBeCapturedBySentry, }); return response.status(statusCode).send({ diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-trigger/triggers/route/exceptions/route-trigger-rest-api-exception-filter.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-trigger/triggers/route/exceptions/route-trigger-rest-api-exception-filter.ts index 43955dae28..d7c58ee392 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-trigger/triggers/route/exceptions/route-trigger-rest-api-exception-filter.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-trigger/triggers/route/exceptions/route-trigger-rest-api-exception-filter.ts @@ -50,6 +50,9 @@ export class RouteTriggerRestApiExceptionFilter implements ExceptionFilter { exception as CustomException, response, 500, + undefined, + undefined, + { shouldBeCapturedBySentry: false }, ); case RouteTriggerExceptionCode.ROUTE_ALREADY_EXIST: case RouteTriggerExceptionCode.ROUTE_PATH_ALREADY_EXIST: diff --git a/packages/twenty-server/src/engine/utils/global-exception-handler.util.ts b/packages/twenty-server/src/engine/utils/global-exception-handler.util.ts index a9500a7761..3e7507a3e9 100644 --- a/packages/twenty-server/src/engine/utils/global-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/utils/global-exception-handler.util.ts @@ -95,14 +95,19 @@ export const handleException = < user, workspace, statusCode, + shouldBeCapturedBySentry = true, }: { exception: T; exceptionHandlerService: ExceptionHandlerService; user?: ExceptionHandlerUser; workspace?: ExceptionHandlerWorkspace; statusCode?: number; + shouldBeCapturedBySentry?: boolean; }): T => { - if (shouldCaptureException(exception, statusCode)) { + if ( + shouldBeCapturedBySentry && + shouldCaptureException(exception, statusCode) + ) { exceptionHandlerService.captureExceptions([exception], { user, workspace }); }