From 40e9e0dde2854784fe4398d18f9f47ecddf48df2 Mon Sep 17 00:00:00 2001 From: Varun Raj Date: Fri, 10 Jul 2026 15:48:29 +0000 Subject: [PATCH] fix: resolve tag via Immich API instead of local DB table in remove_tag The local tag table can lag behind Immich's own tag state (e.g. tags created directly in Immich, or before this app's sync ran), causing remove_tag to silently no-op on a tag that actually exists. Mirror the sibling "tag" action's approach and resolve against the Immich API, matching on the tag's full-path "value" field. Drops the now-unused db/sql local lookup. --- src/lib/workflow/actionExecutor.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib/workflow/actionExecutor.ts b/src/lib/workflow/actionExecutor.ts index 244ecb6..f325cba 100644 --- a/src/lib/workflow/actionExecutor.ts +++ b/src/lib/workflow/actionExecutor.ts @@ -6,7 +6,7 @@ import { exif } from "@/schema"; import { assets } from "@/schema/assets.schema"; import { person } from "@/schema/person.schema"; import { assetFaces } from "@/schema/assetFaces.schema"; -import { eq, and, inArray, sql, desc } from "drizzle-orm"; +import { eq, and, inArray, desc } from "drizzle-orm"; import { IUser } from "@/types/user"; import { getUserHeaders } from "@/helpers/user.helper"; @@ -183,16 +183,15 @@ export async function executeAction( case "remove_tag": { if (!config.tagName) throw new Error("Tag name is required"); try { - // Look up the tag by value (full path for nested tags) without creating it — - // removing a tag that doesn't exist is a no-op, not an error. - const result = await db.execute( - sql`SELECT id FROM "tag" WHERE "userId" = ${user.id} AND "value" = ${config.tagName} LIMIT 1` - ); - const tagId = result.rows?.[0]?.id as string | undefined; - if (!tagId) { + // Look up the tag via Immich's API (same source of truth the "tag" + // action creates/gets from) without creating it — removing a tag + // that doesn't exist is a no-op, not an error. + const tags = await immichFetch("/tags", "GET", undefined, user); + const tag = Array.isArray(tags) ? tags.find((t: any) => t.value === config.tagName) : undefined; + if (!tag) { return { action: "remove_tag", assetsProcessed: 0 }; } - await immichFetch(`/tags/${tagId}/assets`, "DELETE", { ids: assetIds }, user); + await immichFetch(`/tags/${tag.id}/assets`, "DELETE", { ids: assetIds }, user); return { action: "remove_tag", assetsProcessed: assetIds.length }; } catch (e: any) { return { action: "remove_tag", assetsProcessed: 0, error: e.message };