From 01535a3b3eb7cbbc4c71f1576a1cc62ebb450b56 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Mon, 18 May 2026 15:21:42 +0530 Subject: [PATCH] fix(server): handle network errors in RestApiService catch block (#20644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Added safe null check for `err.response?.data?.errors` in `RestApiService.call()` catch block - When the internal HTTP client fails with a network-level error (ECONNREFUSED, timeout), `err.response` is `undefined` — accessing `.data.errors` on it throws a `TypeError` which gets silently swallowed, returning an empty 500 - Now falls back to throwing the raw error message for network failures instead of crashing ## Changes - `packages/twenty-server/src/engine/api/rest/rest-api.service.ts` Fixes #20136 --------- Co-authored-by: Marie Stoppa --- .../src/engine/api/rest/rest-api.service.ts | 9 +++++++-- .../src/filters/unhandled-exception.filter.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/twenty-server/src/engine/api/rest/rest-api.service.ts b/packages/twenty-server/src/engine/api/rest/rest-api.service.ts index 5992aa217c..b26c95ec49 100644 --- a/packages/twenty-server/src/engine/api/rest/rest-api.service.ts +++ b/packages/twenty-server/src/engine/api/rest/rest-api.service.ts @@ -6,6 +6,7 @@ import { type Query } from 'src/engine/api/rest/core/types/query.type'; import { RestApiException } from 'src/engine/api/rest/errors/RestApiException'; import { type RequestContext } from 'src/engine/api/rest/types/RequestContext'; import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; +import { isDefined } from 'twenty-shared/utils'; export enum GraphqlApiType { CORE = 'core', @@ -41,10 +42,14 @@ export class RestApiService { }, }); } catch (err) { - throw new RestApiException(err.response.data.errors); + if (isDefined(err.response?.data?.errors)) { + throw new RestApiException(err.response.data.errors); + } + + throw err; } - if (response.data.errors?.length) { + if (isDefined(response.data.errors) && response.data.errors.length > 0) { throw new RestApiException(response.data.errors); } diff --git a/packages/twenty-server/src/filters/unhandled-exception.filter.ts b/packages/twenty-server/src/filters/unhandled-exception.filter.ts index d92a7ce321..d5ea7b8b7a 100644 --- a/packages/twenty-server/src/filters/unhandled-exception.filter.ts +++ b/packages/twenty-server/src/filters/unhandled-exception.filter.ts @@ -35,6 +35,6 @@ export class UnhandledExceptionFilter implements ExceptionFilter { const status = exception instanceof HttpException ? exception.getStatus() : 500; - response.status(status).json(exception.response); + response.status(status).json(exception.response ?? exception.message); } }