Kareadita/Kavita · warning · KavitaException

invalid-payload

invalid-payload

Error message

invalid-payload

What it means

Thrown by AnnotationService.CreateAnnotation when the incoming AnnotationDto carries no real highlight: HighlightCount is 0 OR SelectedText is null/empty/whitespace. It is a pre-flight input guard meant to reject empty-text annotation creation. IMPORTANT: the throw sits inside the method's outer try, whose catch (Exception) at line 98 re-wraps every exception as 'annotation-failed-create', so the API consumer never receives 'invalid-payload' — only the generic create-failure code reaches the controller; the true cause is visible solely in the server log (ex is logged with ChapterId/Page).

Source

Thrown at Kavita.Services/AnnotationService.cs:52

        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    /// <summary>
    /// Create a new Annotation for the user against a Chapter
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="dto"></param>
    /// <param name="ct"></param>
    /// <returns></returns>
    /// <exception cref="KavitaException">Message is not localized</exception>
    public async Task<AnnotationDto> CreateAnnotation(int userId, AnnotationDto dto, CancellationToken ct = default)
    {
        try
        {
            if (dto.HighlightCount == 0 || string.IsNullOrWhiteSpace(dto.SelectedText))
            {
                throw new KavitaException("invalid-payload");
            }

            var chapter = await unitOfWork.ChapterRepository.GetChapterAsync(dto.ChapterId, ct: ct) ?? throw new KavitaException("chapter-doesnt-exist");
            var chapterTitle = string.Empty;

            try
            {
                var toc = await bookService.GenerateTableOfContents(chapter);
                var pageTocs = BookChapterItemHelper.GetTocForPage(toc, dto.PageNumber);
                if (pageTocs.Count > 0)
                {
                    chapterTitle = pageTocs[0].Title;
                }
            }
            catch (KavitaException)
            {
                /* Swallow */
            }

View on GitHub (pinned to 9c3e540000)

Solutions

  1. On the client, only call create after SelectedText is non-empty AND HighlightCount > 0.
  2. Add request validation ([Required]/FluentValidation) on AnnotationDto.SelectedText and a [Range(1,...)] on HighlightCount so bad payloads are rejected at the controller boundary with 400.
  3. Refactor CreateAnnotation so the input KavitaException is not swallowed by the catch-all — re-throw it (e.g. catch KavitaException first and rethrow) so the real code surfaces instead of annotation-failed-create.

Example fix

// before (inside the outer try — message is swallowed by catch(Exception) at line 98)
if (dto.HighlightCount == 0 || string.IsNullOrWhiteSpace(dto.SelectedText))
    throw new KavitaException("invalid-payload");

// after — validate before the try, or rethrow inside it
if (dto.HighlightCount == 0 || string.IsNullOrWhiteSpace(dto.SelectedText))
    throw new KavitaException("invalid-payload");
try { /* ... */ }
catch (KavitaException) { throw; }            // preserve the specific code
catch (Exception ex) { logger.LogError(ex, ...); throw new KavitaException("annotation-failed-create"); }
Defensive patterns

Strategy: validation

Validate before calling

// Run before annotationService.CreateAnnotation(...)
if (dto.HighlightCount <= 0 || string.IsNullOrWhiteSpace(dto.SelectedText))
    return BadRequest("Annotation requires selected text and a positive highlight count");

Prevention

When it happens

Trigger: POST /api/annotation/create with a body whose highlightCount is 0 or whose selectedText is null/""/whitespace. Also reached if the DTO round-trip loses selectedText (e.g. client sends only XPath).

Common situations: Front-end bug that fires the save before the user finishes selecting text; programmatic/scripted client that omits selectedText; a highlight that was cleared (count reset to 0) immediately before the create request.

Related errors


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