{"record":{"id":"87200df7839b2321","repo":"Kareadita/Kavita","slug":"smart-filter-already-in-use","errorCode":"smart-filter-already-in-use","errorMessage":"smart-filter-already-in-use","messagePattern":"smart-filter-already-in-use","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"warning","filePath":"Kavita.Services/StreamService.cs","lineNumber":56,"sourceCode":"        return await unitOfWork.UserRepository.GetSideNavStreams(userId, visibleOnly, ct);\n    }\n\n    public async Task<IEnumerable<ExternalSourceDto>> GetExternalSources(int userId, CancellationToken ct = default)\n    {\n        return await unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId, ct);\n    }\n\n    public async Task<DashboardStreamDto> CreateDashboardStreamFromSmartFilter(int userId, int smartFilterId,\n        CancellationToken ct = default)\n    {\n        var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId, AppUserIncludes.DashboardStreams, ct);\n        if (user == null) throw new KavitaException(await localizationService.TranslateAsync(userId, \"no-user\"));\n\n        var smartFilter = await unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId, ct);\n        if (smartFilter == null) throw new KavitaException(await localizationService.TranslateAsync(userId, \"smart-filter-doesnt-exist\"));\n\n        var stream = user.DashboardStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);\n        if (stream != null) throw new KavitaException(await localizationService.TranslateAsync(userId, \"smart-filter-already-in-use\"));\n\n        var maxOrder = user!.DashboardStreams.Max(d => d.Order);\n        var createdStream = new AppUserDashboardStream()\n        {\n            Name = smartFilter.Name,\n            IsProvided = false,\n            StreamType = DashboardStreamType.SmartFilter,\n            Visible = true,\n            Order = maxOrder + 1,\n            SmartFilter = smartFilter\n        };\n\n        user.DashboardStreams.Add(createdStream);\n        unitOfWork.UserRepository.Update(user);\n        await unitOfWork.CommitAsync(ct);\n\n        var ret = new DashboardStreamDto()\n        {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/StreamService.cs#L38-L74","documentation":"Thrown by StreamService.CreateDashboardStreamFromSmartFilter when the user already has a dashboard stream whose SmartFilter.Id equals smartFilterId — each smart filter can appear on the dashboard at most once. The message is translated to 'There is an existing stream with this Smart Filter'. StreamController.AddDashboard does not catch it, so it surfaces as HTTP 500 via ExceptionMiddleware.","triggerScenarios":"POST /api/stream/add-dashboard-stream?smartFilterId=N when a DashboardStream for that smart filter already exists for the user (duplicate add, double-click, retry after a partial success).","commonSituations":"User double-clicks 'add to dashboard'; a previous request succeeded but the client retried due to a network blip; UI state out of sync with server state.","solutions":["Refresh the dashboard layout (GET /api/stream/dashboard) and only offer 'add' for filters not already present.","Disable the add button after the first click until the response returns.","If the user wants to re-add, remove the existing dashboard stream first, then add again."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const dashboard = await api.get('/api/stream/dashboard');\nconst alreadyAdded = dashboard.some(d => d.smartFilterId === smartFilterId);\nif (alreadyAdded) {\n  notify('This Smart Filter is already on your dashboard');\n  return;\n}\nawait api.post(`/api/stream/add-dashboard-stream?smartFilterId=${smartFilterId}`);","typeGuard":"function isAlreadyOnDashboard(filterId: number, dashboard: { smartFilterId?: number }[]): boolean {\n  return dashboard.some(d => d.smartFilterId === filterId);\n}","tryCatchPattern":null,"preventionTips":["Refresh the dashboard layout before offering 'add'.","Disable the add button after first click until the response returns.","Treat 'already in use' as informational, not an error, when it slips through."],"tags":["smart-filter","stream","dashboard","duplicate","validation"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}