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
- 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.
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
- 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.
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
- smart-filter-doesnt-exist
- {comparison} is not applicable for {fieldName}
- Name must be set
- You cannot use the name of a system provided stream
- collection-tag-duplicate
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/87200df7839b2321.
Report an issue: GitHub.