Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code b6bd8c7af9 chore: improve monitoring for Microsoft 400 delta sync errors misclassified as U
Downgraded the log level in `MicrosoftMessageListFetchErrorHandler.handleError` from `logger.error` to `logger.warn`.

**Why:** Microsoft Graph API errors (400, 410, 429, etc.) during message list fetching are expected operational conditions — expired cursors, rate limits, transient failures — that are handled by the exception handler with retry/reset logic. Logging these at `error` level causes noise in error monitoring (Sentry captures them via the `error` log level) and creates false urgency. The `warn` level is appropriate since these are recoverable conditions.

Also improved the log format to include structured fields (`statusCode`, `code`, `message`) instead of `JSON.stringify(error)`, which often produces incomplete output for `GraphError` objects (missing the `body` property). This makes logs more searchable and diagnostic.
2026-03-08 10:17:37 +00:00
Sonarly Claude Code 811a94d588 Microsoft 400 delta sync errors misclassified as UNKNOWN instead of SYNC_CURSOR_ERROR
https://sonarly.com/issue/6202?type=bug

When Microsoft Graph API returns HTTP 400 for a delta message sync request (typically due to an expired/invalid sync cursor), the error is classified as UNKNOWN, permanently failing the message channel instead of resetting the cursor and retrying.

Fix: Changed `parseMicrosoftMessagesImportError` to classify HTTP 400 errors as `SYNC_CURSOR_ERROR` instead of `UNKNOWN`.

**Why:** When Microsoft Graph API returns HTTP 400 for a delta messages endpoint (typically because the stored sync cursor/delta link has expired or become invalid), the system was permanently marking the message channel as `FAILED_UNKNOWN`. With `SYNC_CURSOR_ERROR`, the exception handler in `messaging-import-exception-handler.service.ts` will call `handleSyncCursorErrorException`, which resets the cursor and re-queues the sync — allowing automatic recovery.

This matches the existing pattern for HTTP 410 (Gone), which is also classified as `SYNC_CURSOR_ERROR`, and aligns with how Gmail handles expired history IDs.

Also improved the error message to include `error.code` alongside `error.message`, since the `GraphError.message` is often empty/undefined for 400 responses (as evidenced by the Sentry title ending with `: `).
2026-03-08 10:17:37 +00:00
2 changed files with 5 additions and 3 deletions
@@ -15,7 +15,9 @@ export class MicrosoftMessageListFetchErrorHandler {
// oxlint-disable-next-line @typescripttypescript/no-explicit-any
public handleError(error: any): void {
this.logger.error(`Error fetching message list: ${JSON.stringify(error)}`);
this.logger.warn(
`Error fetching message list: statusCode=${error?.statusCode} code=${error?.code} message=${error?.message}`,
);
const networkError = this.microsoftNetworkErrorHandler.handleError(error);
@@ -13,8 +13,8 @@ export const parseMicrosoftMessagesImportError = (
): MessageImportDriverException => {
if (error.statusCode === 400) {
return new MessageImportDriverException(
`Invalid request to Microsoft Graph API: ${error.message}`,
MessageImportDriverExceptionCode.UNKNOWN,
`Invalid request to Microsoft Graph API - code:${error.code} message:${error.message}`,
MessageImportDriverExceptionCode.SYNC_CURSOR_ERROR,
{ cause: options?.cause },
);
}