{"record":{"id":"84dba727fe79c50c","repo":"linshenkx/prompt-optimizer","slug":"tag-already-exists","errorCode":"TAG_ALREADY_EXISTS","errorMessage":"${error.tag}","messagePattern":"\\$\\{error\\.tag\\}","errorType":"exception","errorClass":"FavoriteTagAlreadyExistsError","httpStatus":null,"severity":"warning","filePath":"packages/core/src/services/favorite/electron-proxy.ts","lineNumber":73,"sourceCode":"      if (error.code === 'FAVORITE_ALREADY_EXISTS') {\n        throw new FavoriteAlreadyExistsError(error.content || '');\n      }\n      if (error.code === 'CATEGORY_NOT_FOUND') {\n        throw new FavoriteCategoryNotFoundError(error.id || '');\n      }\n      if (error.code === 'VALIDATION_ERROR') {\n        throw new FavoriteValidationError(error.message || '');\n      }\n      if (error.code === 'STORAGE_ERROR') {\n        throw new FavoriteStorageError(error.message || '');\n      }\n      // Legacy: category already exists\n      if (error.code === 'CATEGORY_ALREADY_EXISTS') {\n        throw new FavoriteValidationError(error.message || 'Category already exists')\n      }\n      // 标签相关错误\n      if (error.code === 'TAG_ALREADY_EXISTS') {\n        throw new FavoriteTagAlreadyExistsError(error.tag || '');\n      }\n      if (error.code === 'TAG_NOT_FOUND') {\n        throw new FavoriteTagNotFoundError(error.tag || '');\n      }\n      if (error.code === 'TAG_ERROR') {\n        throw new FavoriteTagError(FAVORITE_ERROR_CODES.TAG_ERROR, error.message || '', { details: error.message || '' });\n      }\n      // 数据迁移和导入导出错误\n      if (error.code === 'MIGRATION_ERROR') {\n        throw new FavoriteMigrationError(error.message || '', error.cause);\n      }\n      if (error.code === 'IMPORT_EXPORT_ERROR') {\n        throw new FavoriteImportExportError(error.message || '', error.cause, error.details);\n      }\n      // Fallback: preserve message as details for i18n-friendly error\n      throw new FavoriteError(FAVORITE_ERROR_CODES.STORAGE_ERROR, error.message || 'Unknown error', {\n        details: error.message || 'Unknown error',\n      });","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/favorite/electron-proxy.ts#L55-L91","documentation":"Main process returned TAG_ALREADY_EXISTS; converted to FavoriteTagAlreadyExistsError carrying the tag name. Creating a tag that already exists is rejected.","triggerScenarios":"createTag/addTags with a tag name already present in the tag store, via the Electron proxy.","commonSituations":"Concurrent tag creation from two windows; import containing existing tags; UI not deduping user input against the current tag list.","solutions":["Fetch existing tags and skip creation when the name matches","Catch FavoriteTagAlreadyExistsError and treat as success (idempotent create)","Trim/normalize tag names before comparing","Debounce duplicate create requests"],"exampleFix":"// before\nawait mgr.createTag(tag);\n// after\ntry { await mgr.createTag(tag); }\ncatch (e) { if (!(e instanceof FavoriteTagAlreadyExistsError)) throw e; }","handlingStrategy":"try-catch","validationCode":"const tags = new Set((await mgr.getTags?.() ?? []).map(t => t.name));\nif (!tags.has(tag)) await mgr.createTag(tag);","typeGuard":"const isTagAlreadyExists = (e: unknown): e is FavoriteTagAlreadyExistsError => e instanceof FavoriteTagAlreadyExistsError;","tryCatchPattern":"try { await mgr.createTag(tag); } catch (e) { if (isTagAlreadyExists(e)) return; throw e; }","preventionTips":["Treat tag creation as idempotent","Normalize/trim tag names before compare","Refresh tag lists after concurrent edits"],"tags":["favorite","tag","duplicate","electron"],"backgroundTag":"duplicate-entry-rejected","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}