Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 970d8511e1 Microsoft 404 on folder delta endpoint permanently fails email sync
https://sonarly.com/issue/8521?type=bug

When Microsoft Graph API returns HTTP 404 during message list fetch (e.g., deleted folder), the exception handler permanently marks the message channel as FAILED_UNKNOWN instead of retrying, breaking email sync for the affected user.

Fix: ## Fix

The `handleNotFoundException` method was permanently failing the message channel (`FAILED_UNKNOWN`) when a `NOT_FOUND` exception occurred during `MESSAGE_LIST_FETCH`. This was incorrect because Microsoft Graph API legitimately returns HTTP 404 for its folder delta endpoint when a mail folder is deleted, moved, or otherwise inaccessible — which the Microsoft driver maps to `MessageImportDriverExceptionCode.NOT_FOUND`.

The fix removes the special-case branch that treated `NOT_FOUND` during `MESSAGE_LIST_FETCH` as an impossible/fatal state, and instead routes all `NOT_FOUND` exceptions (regardless of sync step) to the retry path: `resetAndMarkAsMessagesListFetchPending`. This is consistent with:
- How `SYNC_CURSOR_ERROR` is handled (also resets and retries)
- The original intent before the Oct 2025 refactor (commit `cd4800eb86d`) introduced the permanent failure path

```typescript file=packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts lines=247-256
private async handleNotFoundException(
    syncStep: MessageImportSyncStep,
    messageChannel: Pick<MessageChannelWorkspaceEntity, 'id'>,
    workspaceId: string,
  ): Promise<void> {
    await this.messageChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending(
      [messageChannel.id],
      workspaceId,
    );
  }
```

The old code for `MESSAGE_LIST_FETCH` (which created a "should never happen" Sentry error and called `markAsFailed`) is entirely removed. When the delta link becomes invalid (folder deleted/moved), the sync cursor resets and the channel queues for a fresh list fetch rather than being permanently broken.
2026-03-04 01:25:20 +00:00
@@ -249,31 +249,6 @@ export class MessageImportExceptionHandlerService {
messageChannel: Pick<MessageChannelWorkspaceEntity, 'id'>,
workspaceId: string,
): Promise<void> {
if (syncStep === MessageImportSyncStep.MESSAGE_LIST_FETCH) {
await this.messageChannelSyncStatusService.markAsFailed(
[messageChannel.id],
workspaceId,
MessageChannelSyncStatus.FAILED_UNKNOWN,
);
this.exceptionHandlerService.captureExceptions(
[
new Error(
'Not Found exception occurred while fetching message list, which should never happen',
),
],
{
additionalData: {
messageChannelId: messageChannel.id,
syncStep,
},
workspace: { id: workspaceId },
},
);
return;
}
await this.messageChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending(
[messageChannel.id],
workspaceId,