Kareadita/Kavita · error · KavitaException

series-doesnt-exist

Error message

series-doesnt-exist

What it means

Thrown by MarkChaptersAsRead when GetSeriesByIdAsync(seriesId) returns null. The literal 'series-doesnt-exist' is a localization key surfaced to the UI. It guards a core read-progress mutation: if the series no longer exists, progress rows cannot be attached to it.

Source

Thrown at Kavita.Services/Reading/ReaderService.cs:95

        user.Progresses ??= new List<AppUserProgress>();
        foreach (var volume in volumes)
        {
            await MarkChaptersAsUnread(user, seriesId, volume.Chapters);
        }
    }

    /// <summary>
    /// Marks all Chapters as Read by creating or updating UserProgress rows. Does not commit.
    /// </summary>
    /// <remarks>Emits events to the UI for each chapter progress and one for each volume progress</remarks>
    /// <param name="user"></param>
    /// <param name="seriesId"></param>
    /// <param name="chapters"></param>
    public async Task MarkChaptersAsRead(AppUser user, int seriesId, IList<Chapter> chapters)
    {
        var seenVolume = new Dictionary<int, bool>();
        var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
        if (series == null) throw new KavitaException("series-doesnt-exist");

        foreach (var chapter in chapters)
        {
            var userProgress = GetUserProgressForChapter(user, chapter);

            if (userProgress == null)
            {
                user.Progresses.Add(new AppUserProgress
                {
                    PagesRead = chapter.Pages,
                    VolumeId = chapter.VolumeId,
                    SeriesId = seriesId,
                    ChapterId = chapter.Id,
                    LibraryId = series.LibraryId,
                });
            }
            else
            {

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Refresh the library list in the client to confirm the series still exists and is in an accessible library.
  2. Verify the seriesId sent matches an existing Series row (and the user has library access).
  3. If the series was deleted, discard the stale client reference and stop the mark-read call.
  4. Trigger a library scan to ensure the series is re-imported if it should exist.

Example fix

// before
await readerService.MarkChaptersAsRead(user, seriesId, chapters);

// after: validate existence first
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
if (series == null) return NotFound("series-doesnt-exist");
await readerService.MarkChaptersAsRead(user, seriesId, chapters);
Defensive patterns

Strategy: validation

Validate before calling

var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
if (series == null) return NotFound();
await readerService.MarkChaptersAsRead(user, seriesId, chapters);

Type guard

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

Try / catch

try { await readerService.MarkChaptersAsRead(user, seriesId, chapters); }
catch (KavitaException ex) when (ex.Message == "series-doesnt-exist")
{ return NotFound("Series no longer exists"); }

Prevention

When it happens

Trigger: A client requests marking chapters read for a seriesId that was deleted, that belongs to a library the user lacks access to (so the query returns nothing), or a stale/cached seriesId from before a library rescan that removed it.

Common situations: User had a series open in the web reader while an admin deleted it or rescanned it away; a third-party client cached an old series id; the series was moved between libraries and the old id reference is stale.

Related errors


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