diff --git a/apps/web/utils/email/google.ts b/apps/web/utils/email/google.ts index 1d96b7efd3..59b237e1d2 100644 --- a/apps/web/utils/email/google.ts +++ b/apps/web/utils/email/google.ts @@ -858,7 +858,11 @@ export class GmailProvider implements EmailProvider { addLabelIds?: string[]; removeLabelIds?: string[]; }) { - return createFilter({ gmail: this.client, ...options }); + return createFilter({ + gmail: this.client, + ...options, + logger: this.logger, + }); } async createAutoArchiveFilter(options: { @@ -869,6 +873,7 @@ export class GmailProvider implements EmailProvider { gmail: this.client, from: options.from, gmailLabelId: options.gmailLabelId, + logger: this.logger, }); } diff --git a/apps/web/utils/gmail/filter.ts b/apps/web/utils/gmail/filter.ts index 12935c3423..32140328dc 100644 --- a/apps/web/utils/gmail/filter.ts +++ b/apps/web/utils/gmail/filter.ts @@ -1,14 +1,17 @@ import type { gmail_v1 } from "@googleapis/gmail"; import { GmailLabel } from "@/utils/gmail/label"; import { extractErrorInfo, withGmailRetry } from "@/utils/gmail/retry"; +import { SafeError } from "@/utils/error"; +import type { Logger } from "@/utils/logger"; export async function createFilter(options: { gmail: gmail_v1.Gmail; from: string; addLabelIds?: string[]; removeLabelIds?: string[]; + logger: Logger; }) { - const { gmail, from, addLabelIds, removeLabelIds } = options; + const { gmail, from, addLabelIds, removeLabelIds, logger } = options; try { return await withGmailRetry(() => @@ -25,6 +28,38 @@ export async function createFilter(options: { ); } catch (error) { if (isFilterExistsError(error)) return { status: 200 }; + + const errorInfo = extractErrorInfo(error); + + logger.error("Failed to create Gmail filter", { + from, + addLabelIds, + removeLabelIds, + error, + }); + + // Check if it might be a filter limit issue + // Documentation says 400/403, but we've seen 500 in production + if ( + errorInfo.status === 500 || + errorInfo.status === 403 || + errorInfo.status === 400 + ) { + try { + const filters = await getFiltersList({ gmail }); + const filterCount = filters.data?.filter?.length ?? 0; + if (filterCount >= 990) { + throw new SafeError( + `Gmail filter limit reached (${filterCount}/1000 filters). Please delete some existing filters in Gmail settings.`, + ); + } + } catch (limitCheckError) { + if (limitCheckError instanceof SafeError) throw limitCheckError; + // If limit check fails, just log and continue with original error + logger.warn("Failed to check filter count", { error: limitCheckError }); + } + } + throw error; } } @@ -33,10 +68,12 @@ export async function createAutoArchiveFilter({ gmail, from, gmailLabelId, + logger, }: { gmail: gmail_v1.Gmail; from: string; gmailLabelId?: string; + logger: Logger; }) { try { return await createFilter({ @@ -44,6 +81,7 @@ export async function createAutoArchiveFilter({ from, removeLabelIds: [GmailLabel.INBOX], addLabelIds: gmailLabelId ? [gmailLabelId] : undefined, + logger, }); } catch (error) { if (isFilterExistsError(error)) return { status: 200 };