Kareadita/Kavita · warning · KavitaException
sidenav-stream-doesnt-exist
Error message
sidenav-stream-doesnt-exist
What it means
Thrown by StreamService.UpdateSideNavStream when GetSideNavStream(dto.Id) returns null, i.e. no AppUserSideNavStream exists with the given Id. This method updates only the Visible flag; notably the repository lookup is by stream Id alone and performs NO ownership (AppUserId) check, so the same error covers both 'does not exist' and 'belongs to another user' only indirectly (a foreign user's existing stream id would NOT throw here). As a plain KavitaException it surfaces as HTTP 500 with the localized 'SideNav Stream doesn't exist' message via ExceptionMiddleware.
Source
Thrown at Kavita.Services/StreamService.cs:244
{
Host = externalSource.Host,
Id = externalSource.Id,
Name = externalSource.Name,
ApiKey = externalSource.ApiKey
}
};
await eventHub.SendMessageToAsync(MessageFactory.SideNavUpdate, MessageFactory.SideNavUpdateEvent(userId),
userId, ct);
return ret;
}
public async Task UpdateSideNavStream(int userId, SideNavStreamDto dto, CancellationToken ct = default)
{
var stream = await unitOfWork.UserRepository.GetSideNavStream(dto.Id, ct);
if (stream == null)
throw new KavitaException(await localizationService.TranslateAsync(userId, "sidenav-stream-doesnt-exist"));
stream.Visible = dto.Visible;
unitOfWork.UserRepository.Update(stream);
await unitOfWork.CommitAsync(ct);
await eventHub.SendMessageToAsync(MessageFactory.SideNavUpdate, MessageFactory.SideNavUpdateEvent(userId),
userId, ct);
}
public async Task UpdateSideNavStreamPosition(int userId, UpdateStreamPositionDto dto, CancellationToken ct = default)
{
var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId,
AppUserIncludes.SideNavStreams, ct);
var stream = user?.SideNavStreams.FirstOrDefault(d => d.Id == dto.Id);
if (stream == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "sidenav-stream-doesnt-exist"));
if (stream.Order == dto.ToPosition) return;
View on GitHub (pinned to 9c3e540000)
Solutions
- Refresh the side-nav list (GET /api/streams/sidenav) and discard local state for any stream whose Id no longer appears before issuing the visibility update.
- Validate that dto.Id is a positive integer and exists in the current client-side list before calling.
- If the row is gone, remove it from the UI instead of retrying the update.
Example fix
// before
toggle(stream: SideNavStreamDto) {
return this.http.post('streams/update-sidenav-stream', { ...stream, visible: !stream.visible });
}
// after
toggle(stream: SideNavStreamDto) {
if (!this.currentIds.has(stream.id)) {
this.refreshSidenav();
return EMPTY;
}
return this.http.post('streams/update-sidenav-stream', { id: stream.id, visible: !stream.visible });
} Defensive patterns
Strategy: validation
Validate before calling
function canUpdateSideNavStream(currentIds: Set<number>, id: number | undefined): boolean {
return typeof id === 'number' && id > 0 && currentIds.has(id);
} Type guard
function isExistingSideNavStreamId(s: unknown, ids: Set<number>): s is number {
return typeof s === 'number' && ids.has(s);
} Try / catch
// treat a 500 'sidenav-stream-doesnt-exist' as a stale-id signal delete stream from local list, then refresh; do not retry with the same id
Prevention
- Track the live set of side-nav ids and drop edits for ids that vanish.
- Re-fetch the side-nav after a delete on another device before toggling visibility.
- Validate the dto.id is a positive integer client-side.
When it happens
Trigger: POST /api/streams/update-sidenav-stream with a SideNavStreamDto whose Id does not match any AppUserSideNavStream row (StreamController.UpdateSideNavStream -> StreamService.UpdateSideNavStream). Typical after the stream was deleted server-side or the dto.Id is stale/wrong.
Common situations: Toggling visibility from a side-nav list that was not refreshed after another session/device deleted the stream; a malformed client payload sending id: 0 or undefined; concurrent deletion between read and write.
Related errors
- external-source-doesnt-exist
- library-doesnt-exist
- profile-does-not-exist
- Path not found in CBL repository: {path}
- series-doesnt-exist
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/e6224515294dde7b.
Report an issue: GitHub.