Kareadita/Kavita · warning · KavitaException

smart-filter-already-in-use

smart-filter-already-in-use

Error message

smart-filter-already-in-use

What it means

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.

Source

Thrown at Kavita.Services/StreamService.cs:56

        return await unitOfWork.UserRepository.GetSideNavStreams(userId, visibleOnly, ct);
    }

    public async Task<IEnumerable<ExternalSourceDto>> GetExternalSources(int userId, CancellationToken ct = default)
    {
        return await unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId, ct);
    }

    public async Task<DashboardStreamDto> CreateDashboardStreamFromSmartFilter(int userId, int smartFilterId,
        CancellationToken ct = default)
    {
        var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId, AppUserIncludes.DashboardStreams, ct);
        if (user == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "no-user"));

        var smartFilter = await unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId, ct);
        if (smartFilter == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "smart-filter-doesnt-exist"));

        var stream = user.DashboardStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
        if (stream != null) throw new KavitaException(await localizationService.TranslateAsync(userId, "smart-filter-already-in-use"));

        var maxOrder = user!.DashboardStreams.Max(d => d.Order);
        var createdStream = new AppUserDashboardStream()
        {
            Name = smartFilter.Name,
            IsProvided = false,
            StreamType = DashboardStreamType.SmartFilter,
            Visible = true,
            Order = maxOrder + 1,
            SmartFilter = smartFilter
        };

        user.DashboardStreams.Add(createdStream);
        unitOfWork.UserRepository.Update(user);
        await unitOfWork.CommitAsync(ct);

        var ret = new DashboardStreamDto()
        {

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Refresh the dashboard layout (GET /api/stream/dashboard) and only offer 'add' for filters not already present.
  2. Disable the add button after the first click until the response returns.
  3. If the user wants to re-add, remove the existing dashboard stream first, then add again.
Defensive patterns

Strategy: validation

Validate before calling

const dashboard = await api.get('/api/stream/dashboard');
const alreadyAdded = dashboard.some(d => d.smartFilterId === smartFilterId);
if (alreadyAdded) {
  notify('This Smart Filter is already on your dashboard');
  return;
}
await api.post(`/api/stream/add-dashboard-stream?smartFilterId=${smartFilterId}`);

Type guard

function isAlreadyOnDashboard(filterId: number, dashboard: { smartFilterId?: number }[]): boolean {
  return dashboard.some(d => d.smartFilterId === filterId);
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/87200df7839b2321. Report an issue: GitHub.