Kareadita/Kavita · error · KavitaException

collection-doesnt-exist

Error message

collection-doesnt-exist

What it means

Thrown by UpdateTag when the collection tag with the given dto.Id does not exist in the database. GetCollectionAsync(dto.Id) returns null, indicating the collection was deleted or the ID is invalid. It is the first validation in the update flow, checked before ownership and title validation.

Source

Thrown at Kavita.Services/CollectionTagService.cs:37

public class CollectionTagService(IUnitOfWork unitOfWork, IEventHub eventHub, IDirectoryService directoryService) : ICollectionTagService
{
    public async Task<bool> DeleteTag(int tagId, AppUser user, CancellationToken ct = default)
    {
        var collectionTag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(tagId, ct: ct);
        if (collectionTag == null) return true;

        user.Collections.Remove(collectionTag);

        if (!unitOfWork.HasChanges()) return true;

        return await unitOfWork.CommitAsync(ct);
    }


    public async Task<bool> UpdateTag(AppUserCollectionDto dto, int userId, CancellationToken ct = default)
    {
        var existingTag = await unitOfWork.CollectionTagRepository.GetCollectionAsync(dto.Id, ct: ct);
        if (existingTag == null) throw new KavitaException("collection-doesnt-exist");
        if (existingTag.AppUserId != userId) throw new KavitaException("access-denied");

        var title = dto.Title.Trim();
        if (string.IsNullOrEmpty(title)) throw new KavitaException("collection-tag-title-required");

        // Ensure the title doesn't exist on the user's account already
        if (!title.Equals(existingTag.Title) && await unitOfWork.CollectionTagRepository.CollectionExists(dto.Title, userId, ct))
            throw new KavitaException("collection-tag-duplicate");

        existingTag.Items ??= [];
        if (existingTag.Source == ScrobbleProvider.Kavita)
        {
            existingTag.Title = title;
            existingTag.NormalizedTitle = dto.Title.ToNormalized();
        }

        var roles = await unitOfWork.UserRepository.GetRoles(userId, ct);
        if (roles.Contains(PolicyConstants.AdminRole) || roles.Contains(PolicyConstants.PromoteRole))

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Refresh the collections list in the UI and confirm the collection still exists before editing
  2. Verify the collection ID matches one returned by the user's collections endpoint
  3. Handle the KavitaException gracefully in the controller and return a 404 to the client

Example fix

// Guard in the calling controller:
// var collection = await collectionService.GetCollection(dto.Id, userId);
// if (collection == null) return NotFound();
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify collection exists before updating:
// var collection = await unitOfWork.CollectionTagRepository.GetCollectionAsync(dto.Id, ct: ct);
// if (collection == null) return NotFound("Collection not found");
// if (collection.AppUserId != userId) return Forbid();

Try / catch

// try {
//     await collectionTagService.UpdateTag(dto, userId, ct);
//     return Ok();
// } catch (KavitaException ex) {
//     return ex.Message switch {
//         "collection-doesnt-exist" => NotFound(),
//         "access-denied" => Forbid(),
//         _ => BadRequest(ex.Message)
//     };
// }

Prevention

When it happens

Trigger: User edits a collection that was deleted in another session; the collection ID in the request is from a different user; a race condition where the collection is removed between the client loading the edit form and submitting changes.

Common situations: Multiple browser tabs or users managing collections concurrently; stale UI after collections were bulk-deleted; bookmarked URL pointing to a removed collection.

Related errors


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