{"record":{"id":"b896f54c7f649b81","repo":"Kareadita/Kavita","slug":"opds-feed-generation-is-not-implemented-for-annota","errorCode":null,"errorMessage":"OPDS feed generation is not implemented for Annotation smart filters","messagePattern":"OPDS feed generation is not implemented for Annotation smart filters","errorType":"exception","errorClass":"OpdsException","httpStatus":null,"severity":"error","filePath":"Kavita.Services/OpdsService.cs","lineNumber":428,"sourceCode":"\n                foreach (var seriesDto in series)\n                {\n                    feed.Entries.Add(CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl));\n                }\n                AddPagination(feed, series, $\"{prefix}{apiKey}/smart-filters/{request.EntityId}/\");\n                break;\n            case FilterEntityType.ReadingList:\n                var readingLists = await unitOfWork.ReadingListRepository.GetBrowseReadingListDtos(userId, (ReadingListFilterDto) decodedFilter, userParams, ct);\n                foreach (var readingList in readingLists)\n                {\n                    feed.Entries.Add(CreateReadingListFeedEntry(readingList, prefix, apiKey, baseUrl));\n                }\n                AddPagination(feed, readingLists, $\"{prefix}{apiKey}/smart-filters/{request.EntityId}/\");\n                break;\n            case FilterEntityType.Person:\n                throw new OpdsException(\"OPDS feed generation is not implemented for Person smart filters\");\n            case FilterEntityType.Annotation:\n                throw new OpdsException(\"OPDS feed generation is not implemented for Annotation smart filters\");\n        }\n\n        return feed;\n    }\n\n    public async Task<Feed> GetSeriesFromCollection(OpdsItemsFromEntityIdRequest request, CancellationToken ct = default)\n    {\n        var userId = UnpackRequest(request, out var apiKey, out var prefix, out var baseUrl);\n        var collectionId = request.EntityId;\n\n        var tag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(collectionId, ct: ct);\n        if (tag == null || (tag.AppUserId != userId && !tag.Promoted))\n        {\n            throw new OpdsException(await localizationService.TranslateAsync(userId, \"collection-doesnt-exist\"));\n        }\n\n        var series = await unitOfWork.SeriesRepository.GetSeriesDtoForCollectionAsync(collectionId, userId, GetUserParams(request.PageNumber), ct);\n        var seriesMetadatas = await unitOfWork.SeriesRepository.GetSeriesMetadataForIdsAsync(series.Select(s => s.Id), ct);","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/OpdsService.cs#L410-L446","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not access Annotation-type smart filters through OPDS; use the Kavita web UI where they are supported.","Recreate the filter as a Series-type filter if the goal is to find series with annotations.","In the OPDS client, filter out Annotation-type smart filters from the navigation.","File a feature request with Kavita if Annotation OPDS feed support is needed.","When building a custom OPDS client, validate the EntityType before requesting the feed."],"exampleFix":"// No code fix — unsupported feed type by design.\n// If developing an OPDS client, pre-filter:\n\n// var filterMetadata = await GetFilterMetadata(filterId);\n// if (filterMetadata.EntityType is FilterEntityType.Person\n//     or FilterEntityType.Annotation)\n// {\n//     ShowMessage(\"This filter type is not available in OPDS.\");\n//     return;\n// }","handlingStrategy":"validation","validationCode":"// Before requesting an Annotation-type smart filter feed, check the EntityType:\n// var filter = await unitOfWork.AppUserSmartFilterRepository.GetById(filterId, ct);\n// if (filter == null) return NotFound();\n// var decoded = SmartFilterHelper.Decode(filter.Filter);\n// if (decoded.EntityType == FilterEntityType.Annotation)\n// {\n//     return BadRequest(\"Annotation smart filters are not available in OPDS.\");\n// }\n// var feed = await opdsService.ResolveSmartFilter(request, ct);","typeGuard":"// C# enum guard:\nstatic bool IsOpdsSupportedFilterType(FilterEntityType type)\n    => type is FilterEntityType.Series or FilterEntityType.ReadingList;","tryCatchPattern":"// try { var feed = await opdsService.ResolveSmartFilter(request, ct); }\n// catch (OpdsException ex) when (ex.Message.Contains(\"not implemented\"))\n// {\n//     // Annotation feeds are not supported in OPDS.\n//     return BadRequest(\"This smart filter type is not supported in OPDS feeds.\");\n// }","preventionTips":["Do not create Annotation-type smart filters if OPDS access is expected.","In OPDS clients, filter out Annotation-type smart filters from the listing.","Educate users that only Series and ReadingList filters have OPDS feed support.","Check EntityType before requesting the feed in custom OPDS integrations.","File a feature request if Annotation OPDS feed support is needed."],"tags":["opds","smart-filter","unimplemented","annotation","feed","feature-gap"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}