From d92f7a1c4542083b934da4dfc2f816c68ab88dd9 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 02:34:06 +0200 Subject: [PATCH 1/6] logger --- apps/web/utils/actions/rule.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/web/utils/actions/rule.ts b/apps/web/utils/actions/rule.ts index bc44c89bb2..404de4858f 100644 --- a/apps/web/utils/actions/rule.ts +++ b/apps/web/utils/actions/rule.ts @@ -371,6 +371,7 @@ export const createRulesOnboardingAction = actionClient hasDigest: false, draftReply: !!ruleConfiguration.draftReply, provider, + logger, }); return ( @@ -404,6 +405,7 @@ export const createRulesOnboardingAction = actionClient hasDigest: false, draftReply: !!ruleConfiguration.draftReply, provider, + logger, }); return prisma.rule @@ -490,6 +492,7 @@ export const createRulesOnboardingAction = actionClient hasDigest: false, draftReply: false, provider, + logger, }); const promise = prisma.rule @@ -787,6 +790,7 @@ async function getActionsFromCategoryAction({ draftReply, hasDigest, provider, + logger, }: { emailAccountId: string; rule: Rule; @@ -795,6 +799,7 @@ async function getActionsFromCategoryAction({ hasDigest: boolean; draftReply: boolean; provider: string; + logger: Logger; }): Promise { const emailProvider = await createEmailProvider({ emailAccountId, @@ -807,6 +812,13 @@ async function getActionsFromCategoryAction({ labelId: null, }); + logger.info("Resolved label ID during onboarding", { + requestedLabel: label, + resolvedLabelName: labelName, + resolvedLabelId: labelId, + ruleName: rule.name, + }); + let actions: Prisma.ActionCreateManyRuleInput[] = [ { type: ActionType.LABEL, label: labelName, labelId }, ]; @@ -828,6 +840,13 @@ async function getActionsFromCategoryAction({ const folderId = await emailProvider.getOrCreateOutlookFolderIdByName( rule.name, ); + + logger.info("Resolved folder ID during onboarding", { + folderName: rule.name, + resolvedFolderId: folderId, + categoryAction, + }); + actions = [ { type: ActionType.MOVE_FOLDER, From 4f3e9a46bd0af24d4e6c891370bfaa7d8a0ece26 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 02:56:14 +0200 Subject: [PATCH 2/6] Fallback to label name if label id not found --- apps/web/utils/ai/actions.ts | 6 +- .../assistant/process-assistant-email.ts | 16 ++-- apps/web/utils/email/microsoft.ts | 16 +++- apps/web/utils/email/types.ts | 6 +- apps/web/utils/reply-tracker/label-helpers.ts | 91 ++++++++++++------- version.txt | 2 +- 6 files changed, 93 insertions(+), 44 deletions(-) diff --git a/apps/web/utils/ai/actions.ts b/apps/web/utils/ai/actions.ts index 11f60033e6..1a060eb9a1 100644 --- a/apps/web/utils/ai/actions.ts +++ b/apps/web/utils/ai/actions.ts @@ -104,7 +104,11 @@ const label: ActionFunction<{ if (!labelIdToUse) return; - await client.labelMessage({ messageId: email.id, labelId: labelIdToUse }); + await client.labelMessage({ + messageId: email.id, + labelId: labelIdToUse, + labelName: args.label || null, + }); }; const draft: ActionFunction<{ diff --git a/apps/web/utils/assistant/process-assistant-email.ts b/apps/web/utils/assistant/process-assistant-email.ts index f09a8c852e..320112ab07 100644 --- a/apps/web/utils/assistant/process-assistant-email.ts +++ b/apps/web/utils/assistant/process-assistant-email.ts @@ -254,16 +254,20 @@ async function withProcessingLabels( } const labels = results - .map((result) => - result.status === "fulfilled" ? result.value?.id : undefined, - ) + .map((result) => (result.status === "fulfilled" ? result.value : undefined)) .filter(isDefined); if (labels.length) { // Fire and forget the initial labeling - provider.labelMessage({ messageId, labelId: labels[0] }).catch((error) => { - logger.error("Error labeling message", { error }); - }); + provider + .labelMessage({ + messageId, + labelId: labels[0].id, + labelName: labels[0].name, + }) + .catch((error) => { + logger.error("Error labeling message", { error }); + }); } try { diff --git a/apps/web/utils/email/microsoft.ts b/apps/web/utils/email/microsoft.ts index 6ccb163614..2d9ef5a230 100644 --- a/apps/web/utils/email/microsoft.ts +++ b/apps/web/utils/email/microsoft.ts @@ -334,13 +334,25 @@ export class OutlookProvider implements EmailProvider { async labelMessage({ messageId, labelId, + labelName, }: { messageId: string; labelId: string; + labelName: string | null; }) { - const category = await this.getLabelById(labelId); + let category = await this.getLabelById(labelId); + if (!category && labelName) { + logger.warn("Label not found, trying to get by name", { + labelId, + labelName, + }); + category = await this.getLabelByName(labelName); + } + if (!category) { - throw new Error(`Category with ID ${labelId} not found`); + throw new Error( + `Category with ID ${labelId} or name ${labelName} not found`, + ); } // Get current message categories to avoid replacing them diff --git a/apps/web/utils/email/types.ts b/apps/web/utils/email/types.ts index 782f7029bb..5b704bbc60 100644 --- a/apps/web/utils/email/types.ts +++ b/apps/web/utils/email/types.ts @@ -92,7 +92,11 @@ export interface EmailProvider { ownerEmail: string, actionSource: "user" | "automation", ): Promise; - labelMessage(options: { messageId: string; labelId: string }): Promise; + labelMessage(options: { + messageId: string; + labelId: string; + labelName: string | null; + }): Promise; removeThreadLabel(threadId: string, labelId: string): Promise; removeThreadLabels(threadId: string, labelIds: string[]): Promise; draftEmail( diff --git a/apps/web/utils/reply-tracker/label-helpers.ts b/apps/web/utils/reply-tracker/label-helpers.ts index 182a67689d..70916f5abf 100644 --- a/apps/web/utils/reply-tracker/label-helpers.ts +++ b/apps/web/utils/reply-tracker/label-helpers.ts @@ -8,21 +8,27 @@ import { } from "./conversation-status-config"; import { getRuleLabel } from "@/utils/rule/consts"; -type LabelIds = Record; +type LabelIds = Record< + ConversationStatus, + { + labelId: string | null; + label: string | null; + } +>; export async function removeConflictingThreadStatusLabels({ emailAccountId, threadId, systemType, provider, - dbLabelIds: providedDbLabelIds, + dbLabels: providedDbLabels, providerLabels: providedProviderLabels, }: { emailAccountId: string; threadId: string; systemType: ConversationStatus; provider: EmailProvider; - dbLabelIds?: LabelIds; + dbLabels?: LabelIds; providerLabels?: EmailLabel[]; }): Promise { const logger = createScopedLogger("removeConflictingThreadStatusLabels").with( @@ -34,8 +40,8 @@ export async function removeConflictingThreadStatusLabels({ }, ); - const [dbLabelIds, providerLabels] = await Promise.all([ - providedDbLabelIds ?? getLabelIdsFromDb(emailAccountId), + const [dbLabels, providerLabels] = await Promise.all([ + providedDbLabels ?? getLabelsFromDb(emailAccountId), providedProviderLabels ?? provider.getLabels(), ]); @@ -44,15 +50,21 @@ export async function removeConflictingThreadStatusLabels({ for (const type of CONVERSATION_STATUS_TYPES) { if (type === systemType) continue; - let labelId = dbLabelIds[type as ConversationStatus]; - if (!labelId) { - const label = providerLabels.find((l) => l.name === getRuleLabel(type)); - if (!label?.id) { + let label = dbLabels[type as ConversationStatus]; + if (!label) { + const l = providerLabels.find((l) => l.name === getRuleLabel(type)); + if (!l?.id) { continue; } - labelId = label.id; + label = { + labelId: l.id, + label: l.name, + }; + } + if (!label.labelId) { + continue; } - removeLabelIds.push(labelId); + removeLabelIds.push(label.labelId); } if (removeLabelIds.length === 0) { @@ -100,31 +112,41 @@ export async function applyThreadStatusLabel({ provider, }); - const [dbLabelIds, providerLabels] = await Promise.all([ - getLabelIdsFromDb(emailAccountId), + const [dbLabels, providerLabels] = await Promise.all([ + getLabelsFromDb(emailAccountId), provider.getLabels(), ]); const addLabel = async () => { - let targetLabelId = dbLabelIds[systemType]; + let targetLabel = dbLabels[systemType]; - if (!targetLabelId) { + if (!targetLabel) { const label = providerLabels.find((l) => l.name === getRuleLabel(systemType)) || (await provider.createLabel(getRuleLabel(systemType))); - if (label) targetLabelId = label.id; - - if (!targetLabelId) { - logger.error("Failed to get or create target label"); - return; + if (label) { + targetLabel = { + labelId: label.id, + label: label.name, + }; } } + if (!targetLabel?.labelId && !targetLabel?.label) { + logger.error("Failed to get or create target label"); + return; + } + return provider - .labelMessage({ messageId, labelId: targetLabelId }) + .labelMessage({ + messageId, + labelId: targetLabel.labelId ?? "", + labelName: targetLabel.label ?? "", + }) .catch((error) => logger.error("Failed to apply thread status label", { - labelId: targetLabelId, + labelId: targetLabel.labelId, + labelName: targetLabel.label, error, }), ); @@ -136,7 +158,7 @@ export async function applyThreadStatusLabel({ threadId, systemType, provider, - dbLabelIds, + dbLabels, providerLabels, }), addLabel(), @@ -145,7 +167,7 @@ export async function applyThreadStatusLabel({ logger.info("Thread status label applied successfully"); } -async function getLabelIdsFromDb(emailAccountId: string): Promise { +async function getLabelsFromDb(emailAccountId: string): Promise { const rules = await prisma.rule.findMany({ where: { emailAccountId, @@ -155,25 +177,28 @@ async function getLabelIdsFromDb(emailAccountId: string): Promise { systemType: true, actions: { where: { type: ActionType.LABEL }, - select: { type: true, labelId: true }, + select: { type: true, labelId: true, label: true }, }, }, }); - const dbLabelIds: LabelIds = { - TO_REPLY: null, - AWAITING_REPLY: null, - FYI: null, - ACTIONED: null, + const dbLabels: LabelIds = { + TO_REPLY: { labelId: null, label: null }, + AWAITING_REPLY: { labelId: null, label: null }, + FYI: { labelId: null, label: null }, + ACTIONED: { labelId: null, label: null }, }; for (const rule of rules) { if (!rule.systemType) continue; const labelAction = rule.actions.find((a) => a.type === ActionType.LABEL); - if (labelAction?.labelId) { - dbLabelIds[rule.systemType as ConversationStatus] = labelAction.labelId; + if (labelAction?.labelId || labelAction?.label) { + dbLabels[rule.systemType as ConversationStatus] = { + labelId: labelAction.labelId, + label: labelAction.label, + }; } } - return dbLabelIds; + return dbLabels; } diff --git a/version.txt b/version.txt index 782426f763..046dbc5266 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.17.27 +v2.17.28 From 17a8c185c230417ec92366a4679e1878eb4ec5bb Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 03:19:53 +0200 Subject: [PATCH 3/6] fallback to label name --- apps/web/utils/reply-tracker/label-helpers.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/utils/reply-tracker/label-helpers.ts b/apps/web/utils/reply-tracker/label-helpers.ts index 70916f5abf..98c78f8cef 100644 --- a/apps/web/utils/reply-tracker/label-helpers.ts +++ b/apps/web/utils/reply-tracker/label-helpers.ts @@ -51,7 +51,7 @@ export async function removeConflictingThreadStatusLabels({ if (type === systemType) continue; let label = dbLabels[type as ConversationStatus]; - if (!label) { + if (!label.labelId && !label.label) { const l = providerLabels.find((l) => l.name === getRuleLabel(type)); if (!l?.id) { continue; @@ -120,7 +120,8 @@ export async function applyThreadStatusLabel({ const addLabel = async () => { let targetLabel = dbLabels[systemType]; - if (!targetLabel) { + // If we don't have both labelId and label from DB, fetch/create it + if (!targetLabel.labelId && !targetLabel.label) { const label = providerLabels.find((l) => l.name === getRuleLabel(systemType)) || (await provider.createLabel(getRuleLabel(systemType))); @@ -132,7 +133,8 @@ export async function applyThreadStatusLabel({ } } - if (!targetLabel?.labelId && !targetLabel?.label) { + // Error only if we still don't have either field after attempting to fetch/create + if (!targetLabel.labelId && !targetLabel.label) { logger.error("Failed to get or create target label"); return; } @@ -140,8 +142,8 @@ export async function applyThreadStatusLabel({ return provider .labelMessage({ messageId, - labelId: targetLabel.labelId ?? "", - labelName: targetLabel.label ?? "", + labelId: targetLabel.labelId || "", + labelName: targetLabel.label, }) .catch((error) => logger.error("Failed to apply thread status label", { From 676ea4cd49d2319ba4b1a794257408cb3207682c Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:13:50 +0200 Subject: [PATCH 4/6] fallback to label name if id is wrong --- apps/web/utils/ai/actions.ts | 7 +- .../assistant/process-assistant-email.ts | 21 +++--- apps/web/utils/email/google.ts | 51 +++++++++++-- apps/web/utils/email/microsoft.ts | 14 +++- apps/web/utils/email/types.ts | 2 +- apps/web/utils/gmail/label.ts | 14 ++++ apps/web/utils/label.server.ts | 72 +++++++++++++++++++ apps/web/utils/reply-tracker/label-helpers.ts | 25 +++---- 8 files changed, 173 insertions(+), 33 deletions(-) create mode 100644 apps/web/utils/label.server.ts diff --git a/apps/web/utils/ai/actions.ts b/apps/web/utils/ai/actions.ts index 1a060eb9a1..3ffe260110 100644 --- a/apps/web/utils/ai/actions.ts +++ b/apps/web/utils/ai/actions.ts @@ -5,6 +5,7 @@ import type { ActionItem, EmailForAction } from "@/utils/ai/types"; import type { EmailProvider } from "@/utils/email/types"; import { enqueueDigestItem } from "@/utils/digest/index"; import { filterNullProperties } from "@/utils"; +import { labelMessageAndSync } from "@/utils/label.server"; const logger = createScopedLogger("ai-actions"); @@ -79,7 +80,7 @@ const archive: ActionFunction> = async ({ const label: ActionFunction<{ label?: string | null; labelId?: string | null; -}> = async ({ client, email, args }) => { +}> = async ({ client, email, args, emailAccountId }) => { let labelIdToUse = args.labelId; // Lazy migration: If no labelId but label name exists, look it up @@ -104,10 +105,12 @@ const label: ActionFunction<{ if (!labelIdToUse) return; - await client.labelMessage({ + await labelMessageAndSync({ + provider: client, messageId: email.id, labelId: labelIdToUse, labelName: args.label || null, + emailAccountId, }); }; diff --git a/apps/web/utils/assistant/process-assistant-email.ts b/apps/web/utils/assistant/process-assistant-email.ts index 320112ab07..3cf226ce45 100644 --- a/apps/web/utils/assistant/process-assistant-email.ts +++ b/apps/web/utils/assistant/process-assistant-email.ts @@ -7,6 +7,7 @@ import { emailToContent } from "@/utils/mail"; import { isAssistantEmail } from "@/utils/assistant/is-assistant-email"; import { internalDateToDate } from "@/utils/date"; import type { EmailProvider } from "@/utils/email/types"; +import { labelMessageAndSync } from "@/utils/label.server"; type ProcessAssistantEmailArgs = { emailAccountId: string; @@ -30,6 +31,7 @@ export async function processAssistantEmail({ return withProcessingLabels( message.id, provider, + emailAccountId, () => processAssistantEmailInternal({ emailAccountId, @@ -230,6 +232,7 @@ function verifyUserSentEmail({ async function withProcessingLabels( messageId: string, provider: EmailProvider, + emailAccountId: string, fn: () => Promise, logger: Logger, ): Promise { @@ -259,15 +262,15 @@ async function withProcessingLabels( if (labels.length) { // Fire and forget the initial labeling - provider - .labelMessage({ - messageId, - labelId: labels[0].id, - labelName: labels[0].name, - }) - .catch((error) => { - logger.error("Error labeling message", { error }); - }); + labelMessageAndSync({ + provider, + messageId, + labelId: labels[0].id, + labelName: labels[0].name, + emailAccountId, + }).catch((error) => { + logger.error("Error labeling message", { error }); + }); } try { diff --git a/apps/web/utils/email/google.ts b/apps/web/utils/email/google.ts index 0e9f8cdb61..23c36a649f 100644 --- a/apps/web/utils/email/google.ts +++ b/apps/web/utils/email/google.ts @@ -12,6 +12,7 @@ import { getLabel, getLabelById, createLabel, + getOrCreateLabel, getOrCreateInboxZeroLabel, GmailLabel, } from "@/utils/gmail/label"; @@ -269,15 +270,53 @@ export class GmailProvider implements EmailProvider { async labelMessage({ messageId, labelId, + labelName, }: { messageId: string; labelId: string; - }) { - await labelMessage({ - gmail: this.client, - messageId, - addLabelIds: [labelId], - }); + labelName: string | null; + }): Promise<{ usedFallback?: boolean; actualLabelId?: string }> { + try { + await labelMessage({ + gmail: this.client, + messageId, + addLabelIds: [labelId], + }); + + return {}; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + + // Only use fallback for "label not found" errors + if ( + errorMessage.includes("Requested entity was not found") && + labelName + ) { + logger.warn("Label not found by ID, trying to get or create by name", { + labelId, + labelName, + }); + + const label = await getOrCreateLabel({ + gmail: this.client, + name: labelName, + }); + await labelMessage({ + gmail: this.client, + messageId, + addLabelIds: [label.id!], + }); + + return { + usedFallback: true, + actualLabelId: label.id!, + }; + } + + // Re-throw if not a "not found" error or fallback didn't work + throw error; + } } async getDraft(draftId: string): Promise { diff --git a/apps/web/utils/email/microsoft.ts b/apps/web/utils/email/microsoft.ts index 2d9ef5a230..539a4ff7c8 100644 --- a/apps/web/utils/email/microsoft.ts +++ b/apps/web/utils/email/microsoft.ts @@ -339,19 +339,22 @@ export class OutlookProvider implements EmailProvider { messageId: string; labelId: string; labelName: string | null; - }) { + }): Promise<{ usedFallback?: boolean; actualLabelId?: string }> { + let usedFallback = false; let category = await this.getLabelById(labelId); + if (!category && labelName) { - logger.warn("Label not found, trying to get by name", { + logger.warn("Category not found by ID, trying to get by name", { labelId, labelName, }); category = await this.getLabelByName(labelName); + usedFallback = true; } if (!category) { throw new Error( - `Category with ID ${labelId} or name ${labelName} not found`, + `Category with ID ${labelId}${labelName ? ` or name ${labelName}` : ""} not found`, ); } @@ -373,6 +376,11 @@ export class OutlookProvider implements EmailProvider { categories: updatedCategories, }); } + + return { + usedFallback, + actualLabelId: category.id || undefined, + }; } async getDraft(draftId: string): Promise { diff --git a/apps/web/utils/email/types.ts b/apps/web/utils/email/types.ts index 5b704bbc60..8d72375a33 100644 --- a/apps/web/utils/email/types.ts +++ b/apps/web/utils/email/types.ts @@ -96,7 +96,7 @@ export interface EmailProvider { messageId: string; labelId: string; labelName: string | null; - }): Promise; + }): Promise<{ usedFallback?: boolean; actualLabelId?: string }>; removeThreadLabel(threadId: string, labelId: string): Promise; removeThreadLabels(threadId: string, labelIds: string[]): Promise; draftEmail( diff --git a/apps/web/utils/gmail/label.ts b/apps/web/utils/gmail/label.ts index 5f13778889..003c7e7e86 100644 --- a/apps/web/utils/gmail/label.ts +++ b/apps/web/utils/gmail/label.ts @@ -248,6 +248,20 @@ export async function getLabelById(options: { return (await gmail.users.labels.get({ userId: "me", id })).data; } +export async function getOrCreateLabel({ + gmail, + name, +}: { + gmail: gmail_v1.Gmail; + name: string; +}) { + if (!name?.trim()) throw new Error("Label name cannot be empty"); + const label = await getLabel({ gmail, name }); + if (label) return label; + const createdLabel = await createLabel({ gmail, name }); + return createdLabel; +} + export async function getOrCreateInboxZeroLabel({ gmail, key, diff --git a/apps/web/utils/label.server.ts b/apps/web/utils/label.server.ts new file mode 100644 index 0000000000..619f0d213d --- /dev/null +++ b/apps/web/utils/label.server.ts @@ -0,0 +1,72 @@ +import type { EmailProvider } from "@/utils/email/types"; +import { createScopedLogger } from "@/utils/logger"; +import prisma from "@/utils/prisma"; + +/** + * Labels a message and automatically updates the database if a stale label ID was detected and fixed. + * + * This handles the case where labels/categories are deleted and recreated with new IDs: + * - Tries to label with the provided ID + * - If that fails and labelName is provided, falls back to looking up by name + * - If the actual ID used differs from the stored ID, updates ALL Actions with that stale ID + */ +export async function labelMessageAndSync({ + provider, + messageId, + labelId, + labelName, + emailAccountId, +}: { + provider: EmailProvider; + messageId: string; + labelId: string; + labelName: string | null; + emailAccountId: string; +}): Promise { + const logger = createScopedLogger("label.server").with({ + provider, + messageId, + labelId, + labelName, + emailAccountId, + }); + + const result = await provider.labelMessage({ + messageId, + labelId, + labelName, + }); + + // If we had to use fallback and got a different ID, update all Actions with the stale ID + if ( + result.usedFallback && + result.actualLabelId && + result.actualLabelId !== labelId + ) { + logger.info("Detected stale label ID, updating all instances in database", { + oldLabelId: labelId, + newLabelId: result.actualLabelId, + }); + + try { + const updateResult = await prisma.action.updateMany({ + where: { + labelId, + rule: { emailAccountId }, + }, + data: { labelId: result.actualLabelId }, + }); + + logger.info("Updated stale label IDs across all actions", { + newLabelId: result.actualLabelId, + updatedCount: updateResult.count, + }); + } catch (error) { + // Don't fail the whole operation if DB update fails + logger.error("Failed to update stale label IDs", { + newLabelId: result.actualLabelId, + error, + }); + } + } +} diff --git a/apps/web/utils/reply-tracker/label-helpers.ts b/apps/web/utils/reply-tracker/label-helpers.ts index 98c78f8cef..63b3849f74 100644 --- a/apps/web/utils/reply-tracker/label-helpers.ts +++ b/apps/web/utils/reply-tracker/label-helpers.ts @@ -7,6 +7,7 @@ import { type ConversationStatus, } from "./conversation-status-config"; import { getRuleLabel } from "@/utils/rule/consts"; +import { labelMessageAndSync } from "@/utils/label.server"; type LabelIds = Record< ConversationStatus, @@ -139,19 +140,19 @@ export async function applyThreadStatusLabel({ return; } - return provider - .labelMessage({ - messageId, - labelId: targetLabel.labelId || "", + return labelMessageAndSync({ + provider, + messageId, + labelId: targetLabel.labelId || "", + labelName: targetLabel.label, + emailAccountId, + }).catch((error) => + logger.error("Failed to apply thread status label", { + labelId: targetLabel.labelId, labelName: targetLabel.label, - }) - .catch((error) => - logger.error("Failed to apply thread status label", { - labelId: targetLabel.labelId, - labelName: targetLabel.label, - error, - }), - ); + error, + }), + ); }; await Promise.all([ From 42ee02061f30dd7655e5edfea94ccf6cdbd45992 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:12:14 +0200 Subject: [PATCH 5/6] fix logs --- apps/web/utils/label.server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/utils/label.server.ts b/apps/web/utils/label.server.ts index 619f0d213d..908f1500ba 100644 --- a/apps/web/utils/label.server.ts +++ b/apps/web/utils/label.server.ts @@ -24,7 +24,7 @@ export async function labelMessageAndSync({ emailAccountId: string; }): Promise { const logger = createScopedLogger("label.server").with({ - provider, + provider: provider.name, messageId, labelId, labelName, From c2cb81d23a4116c859a8d3fa59ca150829b96e54 Mon Sep 17 00:00:00 2001 From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:19:12 +0200 Subject: [PATCH 6/6] fix tests --- apps/web/utils/reply-tracker/label-helpers.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/utils/reply-tracker/label-helpers.test.ts b/apps/web/utils/reply-tracker/label-helpers.test.ts index ce27757133..23a461d53c 100644 --- a/apps/web/utils/reply-tracker/label-helpers.test.ts +++ b/apps/web/utils/reply-tracker/label-helpers.test.ts @@ -261,7 +261,8 @@ describe("applyThreadStatusLabel", () => { // Should use the newly created label ID expect(mockProvider.labelMessage).toHaveBeenCalledWith({ messageId, - labelId: "label-to-reply", // From createLabel mock + labelId: "label-to-reply", + labelName: "To Reply", }); });