diff --git a/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts b/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts index 5408778f972..7467ba3569f 100644 --- a/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts +++ b/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts @@ -5,6 +5,7 @@ import { msg } from '@lingui/core/macro'; import { ImapFlow } from 'imapflow'; import { createTransport } from 'nodemailer'; import { ConnectedAccountProvider } from 'twenty-shared/types'; +import { MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception'; import { Repository } from 'typeorm'; import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -53,6 +54,16 @@ export class ImapSmtpCaldavService { }, }); + client.on('error', (error) => { + if (this.isExpectedSocketError(error)) { + this.logger.warn(`IMAP test client socket error: ${error.message}`); + + return; + } + + this.logger.error('IMAP test client emitted an async error', error.stack); + }); + try { await client.connect(); @@ -220,4 +231,17 @@ export class ImapSmtpCaldavService { authContext, ); } + + private isExpectedSocketError(error: Error): boolean { + if (!('code' in error)) { + return false; + } + + return [ + MessageNetworkExceptionCode.ECONNABORTED, + MessageNetworkExceptionCode.ECONNRESET, + MessageNetworkExceptionCode.ETIMEDOUT, + MessageNetworkExceptionCode.EHOSTUNREACH, + ].includes(error.code as MessageNetworkExceptionCode); + } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider.ts index 65747593481..539109c9132 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider.ts @@ -8,6 +8,7 @@ import { type ImapSmtpCaldavParams } from 'src/engine/core-modules/imap-smtp-cal import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service'; import { type ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity'; import { MessageImportDriverExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception'; +import { MessageNetworkExceptionCode } from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-network.exception'; import { parseImapAuthenticationError } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-imap-authentication-error.util'; type ConnectedAccountIdentifier = Pick< @@ -18,6 +19,7 @@ type ConnectedAccountIdentifier = Pick< @Injectable() export class ImapClientProvider { private readonly logger = new Logger(ImapClientProvider.name); + private readonly clientsInShutdown = new WeakSet(); private static readonly CONNECTION_TIMEOUT_MS = 30000; private static readonly GREETING_TIMEOUT_MS = 16000; @@ -42,11 +44,19 @@ export class ImapClientProvider { } async closeClient(client: ImapFlow): Promise { + this.clientsInShutdown.add(client); + try { await client.logout(); this.logger.log('Closed IMAP client'); } catch (error) { - this.logger.error(`Error closing IMAP client: ${error.message}`); + if (this.isExpectedSocketError(error)) { + this.logger.warn(`IMAP client closed with a socket error: ${error.message}`); + } else { + this.logger.error(`Error closing IMAP client: ${error.message}`); + } + } finally { + this.clientsInShutdown.delete(client); } } @@ -94,6 +104,18 @@ export class ImapClientProvider { greetingTimeout: ImapClientProvider.GREETING_TIMEOUT_MS, }); + client.on('error', (error) => { + const logContext = `IMAP client error for ${connectedAccount.handle}`; + + if (this.clientsInShutdown.has(client)) { + this.logger.warn(`${logContext} during client shutdown: ${error.message}`); + + return; + } + + this.logger.error(logContext, error.stack); + }); + try { await client.connect(); @@ -112,4 +134,17 @@ export class ImapClientProvider { throw error; } } + + private isExpectedSocketError(error: Error): boolean { + if (!('code' in error)) { + return false; + } + + return [ + MessageNetworkExceptionCode.ECONNABORTED, + MessageNetworkExceptionCode.ECONNRESET, + MessageNetworkExceptionCode.ETIMEDOUT, + MessageNetworkExceptionCode.EHOSTUNREACH, + ].includes(error.code as MessageNetworkExceptionCode); + } }