Kareadita/Kavita · error · KavitaException

series-doesnt-exist

series-doesnt-exist

Error message

series-doesnt-exist

What it means

Thrown by SeriesService.GetSeriesDetail when GetSeriesDtoByIdAsync returns null for the given seriesId. It uses the localization service to translate 'series-doesnt-exist' per user, so the message is localized rather than hard-coded. It signals the series was deleted, never existed, or is not visible to the lookup.

Source

Thrown at Kavita.Services/SeriesService.cs:528

        }
        catch (Exception ex)
        {
            logger.LogError(ex, "There was an issue when trying to delete multiple series");
            return false;
        }
    }

    /// <summary>
    /// This generates all the arrays needed by the Series Detail page in the UI. It is a specialized API for the unique layout constraints.
    /// </summary>
    /// <param name="seriesId"></param>
    /// <param name="userId"></param>
    /// <param name="ct"></param>
    /// <returns></returns>
    public async Task<SeriesDetailDto> GetSeriesDetail(int seriesId, int userId, CancellationToken ct = default)
    {
        var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId, ct);
        if (series == null) throw new KavitaException(await localizationService.TranslateAsync(userId, "series-doesnt-exist"));

        var libraryIds = await unitOfWork.LibraryRepository.GetLibraryIdsForUserIdAsync(userId, ct: ct);
        if (!libraryIds.Contains(series.LibraryId))
            throw new UnauthorizedAccessException("user-no-access-library-from-series");

        var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId, ct: ct);
        if (user!.AgeRestriction != AgeRating.NotApplicable)
        {
            var seriesMetadata = await unitOfWork.SeriesRepository.GetSeriesMetadataAsync(seriesId, ct);
            if (seriesMetadata!.AgeRating > user.AgeRestriction)
                throw new UnauthorizedAccessException("series-restricted-age-restriction");
        }


        var libraryType = await unitOfWork.LibraryRepository.GetLibraryTypeAsync(series.LibraryId, ct);
        var volumes = await unitOfWork.VolumeRepository.GetVolumesDtoAsync(seriesId, userId, ct: ct);
        var namingContext = await LocalizedNamingContext.CreateAsync(namingService, localizationService, userId, libraryType);
        var bookTreatment = libraryType is LibraryType.Book or LibraryType.LightNovel;

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Confirm the seriesId still exists via a series list call before opening detail.
  2. Handle the localized KavitaException in the client and navigate the user back to the library.
  3. If the series should exist, verify the user's library access and that the series wasn't filtered out.
  4. Clear stale client-side caches/links pointing at removed series.

Example fix

// before
var detail = await seriesService.GetSeriesDetail(seriesId, userId);

// after
var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId, ct);
if (series is null) return NotFound(localizer["series-doesnt-exist"]);
var detail = await seriesService.GetSeriesDetail(seriesId, userId, ct);
Defensive patterns

Strategy: validation

Validate before calling

var series = await unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId, ct);
if (series is null) return NotFound(localizer["series-doesnt-exist"]);
var detail = await seriesService.GetSeriesDetail(seriesId, userId, ct);

Type guard

static bool SeriesExists(SeriesDto? s) => s is not null;

Try / catch

try { return await seriesService.GetSeriesDetail(seriesId, userId, ct); }
catch (KavitaException ex) when (ex.Message.Contains("series-doesnt-exist"))
{ return NotFound(ex.Message); }

Prevention

When it happens

Trigger: GET series detail for a seriesId that returns no DTO - series was deleted, id is wrong, or the query (which is user/library scoped) found no row.

Common situations: Stale client bookmark to a deleted series; race between deletion and a detail fetch; wrong seriesId from a malformed link; or the user has no access and the scoped query returns null.

Related errors


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