From 8fb8b5d2f1e03cbc1d0bdfae63395acab4fa2271 Mon Sep 17 00:00:00 2001 From: neo773 <62795688+neo773@users.noreply.github.com> Date: Wed, 28 Jan 2026 01:53:29 +0530 Subject: [PATCH] fix message channels stuck in ONGOING (#17492) `markAsMessagesListFetchOngoing()` was missing `syncStageStartedAt` causing stuck channels to be never recovered. Regression was caused by commit [68a9ef57f0](https://github.com/twentyhq/twenty/commit/68a9ef57f0) --- .../jobs/calendar-ongoing-stale.job.ts | 5 +---- .../utils/is-sync-stale.util.ts | 8 +++++++- .../calendar-channel-sync-status.service.ts | 1 + .../message-channel-sync-status.service.ts | 2 ++ .../jobs/messaging-ongoing-stale.job.ts | 5 +---- .../utils/__tests__/is-sync-stale.util.spec.ts | 14 +++++++++++++- .../utils/is-sync-stale.util.ts | 8 +++++++- 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts index cae524cbd2..887e6aa200 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts @@ -54,10 +54,7 @@ export class CalendarOngoingStaleJob { }); for (const calendarChannel of calendarChannels) { - if ( - calendarChannel.syncStageStartedAt && - isSyncStale(calendarChannel.syncStageStartedAt) - ) { + if (isSyncStale(calendarChannel.syncStageStartedAt)) { await this.calendarChannelSyncStatusService.resetSyncStageStartedAt( [calendarChannel.id], workspaceId, diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/is-sync-stale.util.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/is-sync-stale.util.ts index a204ae83e2..9f5cff6823 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/is-sync-stale.util.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/is-sync-stale.util.ts @@ -1,6 +1,12 @@ +import { isDefined } from 'twenty-shared/utils'; + import { CALENDAR_IMPORT_ONGOING_SYNC_TIMEOUT } from 'src/modules/calendar/calendar-event-import-manager/constants/calendar-import-ongoing-sync-timeout.constant'; -export const isSyncStale = (syncStageStartedAt: string): boolean => { +export const isSyncStale = (syncStageStartedAt?: string | null): boolean => { + if (!isDefined(syncStageStartedAt)) { + return false; + } + const syncStageStartedTime = new Date(syncStageStartedAt).getTime(); if (isNaN(syncStageStartedTime)) { diff --git a/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts b/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts index 166888abae..b6f6f3de14 100644 --- a/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts +++ b/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts @@ -182,6 +182,7 @@ export class CalendarChannelSyncStatusService { await calendarChannelRepository.update(calendarChannelIds, { syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, syncStatus: CalendarChannelSyncStatus.ONGOING, + syncStageStartedAt: new Date().toISOString(), }); }, authContext); } diff --git a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts index 81d8923260..d18e0486b0 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts @@ -199,6 +199,7 @@ export class MessageChannelSyncStatusService { await messageChannelRepository.update(messageChannelIds, { syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, syncStatus: MessageChannelSyncStatus.ONGOING, + syncStageStartedAt: new Date().toISOString(), }); }, authContext); } @@ -277,6 +278,7 @@ export class MessageChannelSyncStatusService { await messageChannelRepository.update(messageChannelIds, { syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, + syncStatus: MessageChannelSyncStatus.ONGOING, syncStageStartedAt: new Date().toISOString(), }); }, authContext); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts index 701acd366e..561850742c 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts @@ -54,10 +54,7 @@ export class MessagingOngoingStaleJob { }); for (const messageChannel of messageChannels) { - if ( - messageChannel.syncStageStartedAt && - isSyncStale(messageChannel.syncStageStartedAt) - ) { + if (isSyncStale(messageChannel.syncStageStartedAt)) { await this.messageChannelSyncStatusService.resetSyncStageStartedAt( [messageChannel.id], workspaceId, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/is-sync-stale.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/is-sync-stale.util.spec.ts index 9935eb6108..0770473919 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/is-sync-stale.util.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/is-sync-stale.util.spec.ts @@ -24,7 +24,19 @@ describe('isSyncStale', () => { expect(result).toBe(false); }); - it('should return false if syncStageStartedAt is invalid', () => { + it('should return false if syncStageStartedAt is undefined', () => { + const result = isSyncStale(undefined); + + expect(result).toBe(false); + }); + + it('should return false if syncStageStartedAt is null', () => { + const result = isSyncStale(null); + + expect(result).toBe(false); + }); + + it('should throw an error if syncStageStartedAt is invalid', () => { const syncStageStartedAt = 'invalid-date'; expect(() => { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/is-sync-stale.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/is-sync-stale.util.ts index 7a1ae5e202..45661f5dd6 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/is-sync-stale.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/is-sync-stale.util.ts @@ -1,6 +1,12 @@ +import { isDefined } from 'twenty-shared/utils'; + import { MESSAGING_IMPORT_ONGOING_SYNC_TIMEOUT } from 'src/modules/messaging/message-import-manager/constants/messaging-import-ongoing-sync-timeout.constant'; -export const isSyncStale = (syncStageStartedAt: string): boolean => { +export const isSyncStale = (syncStageStartedAt?: string | null): boolean => { + if (!isDefined(syncStageStartedAt)) { + return false; + } + const syncStageStartedTime = new Date(syncStageStartedAt).getTime(); if (isNaN(syncStageStartedTime)) {