{"record":{"id":"8e2cae6da33de0c7","repo":"Kareadita/Kavita","slug":"errors-theme-already-in-use","errorCode":"errors.theme-already-in-use","errorMessage":"errors.theme-already-in-use","messagePattern":"errors\\.theme-already-in-use","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"warning","filePath":"Kavita.Services/SiteThemeService.cs","lineNumber":426,"sourceCode":"    /// <param name=\"tempFile\"></param>\n    /// <param name=\"username\"></param>\n    /// <param name=\"ct\"></param>\n    /// <returns></returns>\n    public async Task<SiteTheme> CreateThemeFromFile(string tempFile, string username, CancellationToken ct = default)\n    {\n        if (!directoryService.FileSystem.File.Exists(tempFile))\n        {\n            logger.LogInformation(\"Unable to create theme from manual upload as file not in temp\");\n            throw new KavitaException(\"errors.theme-manual-upload\");\n        }\n\n\n        var filename = directoryService.FileSystem.FileInfo.New(tempFile).Name;\n        var themeName = Path.GetFileNameWithoutExtension(filename);\n\n        if (await unitOfWork.SiteThemeRepository.GetThemeDtoByName(themeName) != null)\n        {\n            throw new KavitaException(\"errors.theme-already-in-use\");\n        }\n\n        directoryService.CopyFileToDirectory(tempFile, directoryService.SiteThemeDirectory);\n        var finalLocation = directoryService.FileSystem.Path.Join(directoryService.SiteThemeDirectory, filename);\n\n\n        // Create a new entry and note that this is downloaded\n        var theme = new SiteTheme()\n        {\n            Name = Path.GetFileNameWithoutExtension(filename),\n            NormalizedName = themeName.ToNormalized(),\n            FileName = directoryService.FileSystem.Path.GetFileName(finalLocation),\n            Provider = ThemeProvider.Custom,\n            IsDefault = false,\n            Description = $\"Manually uploaded via UI by {username}\",\n            PreviewUrls = string.Empty,\n            Author = username,\n        };","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/SiteThemeService.cs#L408-L444","documentation":"Thrown by SiteThemeService.CreateThemeFromFile when a theme with the same name already exists. The theme name is derived from the uploaded filename without its extension, and GetThemeDtoByName(themeName) returns non-null. KavitaException('errors.theme-already-in-use') is thrown. ThemeController's upload-theme endpoint does not catch it, so it surfaces as HTTP 500 with the raw key 'errors.theme-already-in-use'.","triggerScenarios":"POST /api/theme/upload-theme with a .css file whose base name matches an existing theme name, including shipped built-ins (e.g. 'Dark').","commonSituations":"Re-uploading a theme already installed; uploading a file whose name collides with a built-in or previously-downloaded theme; case-only differences that normalize to the same name.","solutions":["Rename the .css file before uploading so its base name is unique.","List existing themes via GET /api/theme and pick a non-colliding name.","If the existing theme is no longer wanted and not in use, delete it first (see errors.delete-theme-in-use), then re-upload."],"exampleFix":"// before\nupload('dark.css') // collides with built-in Dark\n\n// after\nupload('dark-custom.css') // unique name","handlingStrategy":"validation","validationCode":"// Derive the theme name the same way the server does (filename without extension), then check uniqueness.\nconst baseName = file.name.replace(/\\.css$/i, '');\nconst themes: SiteThemeDto[] = await api.get('/api/theme');\nconst taken = new Set(themes.map(t => t.name.toLowerCase()));\nif (taken.has(baseName.toLowerCase())) {\n  showError('A theme with that name already exists');\n  return;\n}\nawait api.uploadTheme(file);","typeGuard":"function isThemeNameUnique(baseName: string, themes: { name: string }[]): boolean {\n  const taken = new Set(themes.map(t => t.name.toLowerCase()));\n  return !taken.has(baseName.toLowerCase());\n}","tryCatchPattern":null,"preventionTips":["Compare against existing theme names case-insensitively before uploading.","Avoid colliding with built-in names like 'Dark'.","Rename the file rather than deleting an in-use existing theme."],"tags":["theme","validation","upload","duplicate","admin"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}