Kareadita/Kavita · error · OpdsException

collection-doesnt-exist

Error message

collection-doesnt-exist

What it means

Thrown by OpdsService.GetSeriesFromCollection when the requested collection tag is null OR the requesting user does not own it and it is not promoted. The check is twofold: the collection must exist (GetCollectionAsync returns non-null), and if it exists, either AppUserId must match the requesting user or Promoted must be true (making it a public/shared collection). This is both a not-found and an access-control guard.

Source

Thrown at Kavita.Services/OpdsService.cs:442

                break;
            case FilterEntityType.Person:
                throw new OpdsException("OPDS feed generation is not implemented for Person smart filters");
            case FilterEntityType.Annotation:
                throw new OpdsException("OPDS feed generation is not implemented for Annotation smart filters");
        }

        return feed;
    }

    public async Task<Feed> GetSeriesFromCollection(OpdsItemsFromEntityIdRequest request, CancellationToken ct = default)
    {
        var userId = UnpackRequest(request, out var apiKey, out var prefix, out var baseUrl);
        var collectionId = request.EntityId;

        var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(collectionId, ct: ct);
        if (tag == null || (tag.AppUserId != userId && !tag.Promoted))
        {
            throw new OpdsException(await localizationService.TranslateAsync(userId, "collection-doesnt-exist"));
        }

        var series = await unitOfWork.SeriesRepository.GetSeriesDtoForCollectionAsync(collectionId, userId, GetUserParams(request.PageNumber), ct);
        var seriesMetadatas = await unitOfWork.SeriesRepository.GetSeriesMetadataForIdsAsync(series.Select(s => s.Id), ct);

        var feed = CreateFeed(tag.Title + " Collection", $"{apiKey}/collections/{collectionId}", apiKey, prefix);
        SetFeedId(feed, $"collections-{collectionId}");
        AddPagination(feed, series, $"{prefix}{apiKey}/collections/{collectionId}");

        foreach (var seriesDto in series)
        {
            feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl));
        }

        return feed;
    }

    public async Task<Feed> GetSeriesFromLibrary(OpdsItemsFromEntityIdRequest request, CancellationToken ct = default)

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Refresh the collection listing in the OPDS client to get current, accessible collection IDs.
  2. If the collection should be accessible, verify in Kavita's UI that it is Promoted (shared publicly) or owned by the requesting user.
  3. Confirm the correct user's API key is being used; private collections are only visible to their owner.
  4. Recreate the collection if it was accidentally deleted.
  5. Check the CollectionTags table in the database to confirm the collection exists and its Promoted/owner status.

Example fix

// No code fix — access control / not-found issue.
// In Kavita UI: Collections -> edit collection -> toggle 'Promoted'
// to make it accessible to other users via OPDS.

// In the OPDS client, only navigate to collections returned by
// the user's own collections feed.
Defensive patterns

Strategy: validation

Validate before calling

// Before requesting a collection feed, verify access:
// var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(collectionId, ct: ct);
// if (tag == null || (tag.AppUserId != userId && !tag.Promoted))
// {
//     ShowUser("Collection not found or access denied.");
//     return;
// }
// var feed = await opdsService.GetSeriesFromCollection(request, ct);

Try / catch

// try { var feed = await opdsService.GetSeriesFromCollection(request, ct); }
// catch (OpdsException ex)
// {
//     if (ex.Message.Contains("collection-doesnt-exist"))
//     {
//         // Collection was deleted or access revoked.
//         await RefreshCollectionList();
//         return BadRequest(ex.Message);
//     }
//     throw;
// }

Prevention

When it happens

Trigger: An OPDS client requests {apiKey}/collections/{collectionId}. The collection was deleted, the ID is invalid, or the collection is private and belongs to a different user who has not promoted it.

Common situations: A collection was deleted from the Kavita UI but the OPDS reader cached the URL. A user is trying to access another user's private (non-promoted) collection. The collectionId in the URL is stale or corrupted.

Related errors


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