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
- On the client, only call create after SelectedText is non-empty AND HighlightCount > 0.
- 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.
- 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
- Disable the save-annotation UI control until the user has selected non-empty text.
- Add [Required] on AnnotationDto.SelectedText and a positive-range validator on HighlightCount at the DTO boundary.
- Treat the API's 'annotation-failed-create' as a hint to check server logs, since invalid-payload is masked by the catch-all.
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
- chapter-doesnt-exist
- annotation-failed-create
- No images found on the specified page
- Image index {bookmarkDto.ImageOffset} is out of range. Page
- Image element does not have a valid source attribute
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/066150eceb2bb084.
Report an issue: GitHub.