Kareadita/Kavita · error · OpdsException

OPDS feed generation is not implemented for Annotation smart

Error message

OPDS feed generation is not implemented for Annotation smart filters

What it means

Thrown by OpdsService.ResolveSmartFilter when the decoded smart filter has FilterEntityType.Annotation. Like the Person case, the OPDS feed builder has no rendering path for Annotation-type smart filters and explicitly throws. This is a deliberate guard against an unsupported feed type rather than a runtime bug; the switch exhaustively handles all enum values and rejects those without feed-generation logic.

Source

Thrown at Kavita.Services/OpdsService.cs:428

                foreach (var seriesDto in series)
                {
                    feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl));
                }
                AddPagination(feed, series, $"{prefix}{apiKey}/smart-filters/{request.EntityId}/");
                break;
            case FilterEntityType.ReadingList:
                var readingLists = await unitOfWork.ReadingListRepository.GetBrowseReadingListDtos(userId, (ReadingListFilterDto) decodedFilter, userParams, ct);
                foreach (var readingList in readingLists)
                {
                    feed.Entries.Add(CreateReadingListFeedEntry(readingList, prefix, apiKey, baseUrl));
                }
                AddPagination(feed, readingLists, $"{prefix}{apiKey}/smart-filters/{request.EntityId}/");
                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);

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Do not access Annotation-type smart filters through OPDS; use the Kavita web UI where they are supported.
  2. Recreate the filter as a Series-type filter if the goal is to find series with annotations.
  3. In the OPDS client, filter out Annotation-type smart filters from the navigation.
  4. File a feature request with Kavita if Annotation OPDS feed support is needed.
  5. When building a custom OPDS client, validate the EntityType before requesting the feed.

Example fix

// No code fix — unsupported feed type by design.
// If developing an OPDS client, pre-filter:

// var filterMetadata = await GetFilterMetadata(filterId);
// if (filterMetadata.EntityType is FilterEntityType.Person
//     or FilterEntityType.Annotation)
// {
//     ShowMessage("This filter type is not available in OPDS.");
//     return;
// }
Defensive patterns

Strategy: validation

Validate before calling

// Before requesting an Annotation-type smart filter feed, check the EntityType:
// var filter = await unitOfWork.AppUserSmartFilterRepository.GetById(filterId, ct);
// if (filter == null) return NotFound();
// var decoded = SmartFilterHelper.Decode(filter.Filter);
// if (decoded.EntityType == FilterEntityType.Annotation)
// {
//     return BadRequest("Annotation smart filters are not available in OPDS.");
// }
// var feed = await opdsService.ResolveSmartFilter(request, ct);

Type guard

// C# enum guard:
static bool IsOpdsSupportedFilterType(FilterEntityType type)
    => type is FilterEntityType.Series or FilterEntityType.ReadingList;

Try / catch

// try { var feed = await opdsService.ResolveSmartFilter(request, ct); }
// catch (OpdsException ex) when (ex.Message.Contains("not implemented"))
// {
//     // Annotation feeds are not supported in OPDS.
//     return BadRequest("This smart filter type is not supported in OPDS feeds.");
// }

Prevention

When it happens

Trigger: A user creates an Annotation-type smart filter in the Kavita web UI and attempts to open it via an OPDS reader at {apiKey}/smart-filters/{filterId}. The decoded filter's EntityType is Annotation, hitting the throw branch.

Common situations: A user has Annotation-type filters and their OPDS reader crawls all smart-filter feeds. The OPDS reader auto-discovered the filter ID from a prior feed listing and attempts to resolve it.

Related errors


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