{"record":{"id":"b90c250529f5fd70","repo":"Kareadita/Kavita","slug":"generic-reading-list-create","errorCode":"generic-reading-list-create","errorMessage":"generic-reading-list-create","messagePattern":"generic-reading-list-create","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"error","filePath":"Kavita.Services/ReadingLists/ReadingListService.cs","lineNumber":65,"sourceCode":"    /// </summary>\n    /// <param name=\"userWithReadingList\"></param>\n    /// <param name=\"title\"></param>\n    /// <returns></returns>\n    /// <exception cref=\"KavitaException\"></exception>\n    public async Task<ReadingList> CreateReadingListForUser(AppUser userWithReadingList, string title)\n    {\n        // When creating, we need to make sure Title is unique\n        var normalizedTitle = title.ToNormalized();\n        var hasExisting = userWithReadingList.ReadingLists.Any(l => l.NormalizedTitle == normalizedTitle);\n        if (hasExisting)\n        {\n            throw new KavitaException(\"reading-list-name-exists\");\n        }\n\n        var readingList = new ReadingListBuilder(title).Build();\n        userWithReadingList.ReadingLists.Add(readingList);\n\n        if (!unitOfWork.HasChanges()) throw new KavitaException(\"generic-reading-list-create\");\n        await unitOfWork.CommitAsync();\n        return readingList;\n    }\n\n    /// <summary>\n    ///\n    /// </summary>\n    /// <param name=\"readingList\"></param>\n    /// <param name=\"dto\"></param>\n    /// <exception cref=\"KavitaException\"></exception>\n    public async Task UpdateReadingList(ReadingList readingList, UpdateReadingListDto dto)\n    {\n        dto.Title = dto.Title.Trim();\n        if (string.IsNullOrEmpty(dto.Title)) throw new KavitaException(\"reading-list-title-required\");\n\n        if (!dto.Title.Equals(readingList.Title) && await unitOfWork.ReadingListRepository.ReadingListExists(dto.Title, readingList.Id))\n            throw new KavitaException(\"reading-list-name-exists\");\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/ReadingLists/ReadingListService.cs#L47-L83","documentation":"Thrown by CreateReadingListForUser when unitOfWork.HasChanges() is false right after the ReadingListBuilder built the list and added it to the user. A non-empty title should always produce a change, so a 'no changes' result means the builder produced an entity that EF tracks as unchanged - typically because the title was whitespace/empty or the builder produced a default instance with no tracked mutations.","triggerScenarios":"CreateReadingListForUser called with a title that, after building and adding, leaves the DbContext with zero pending changes - e.g. an empty/whitespace title that the builder ignored, or a builder misconfiguration that produced an already-tracked/default entity.","commonSituations":"Frontend sent an empty or whitespace-only title that slipped past the uniqueness check (empty string is unique the first time); a ReadingListBuilder refactor stopped setting a tracked field; or the user entity passed in was detached so the Add produced no change.","solutions":["Validate that title is non-empty/non-whitespace before calling create (reject early with reading-list-title-required semantics).","Confirm the AppUser passed in is tracked by the same DbContext/unitOfWork.","Inspect ReadingListBuilder.Build() to ensure it sets at least Title/NormalizedTitle so EF sees a change.","Unit-test the builder with a known title to assert unitOfWork.HasChanges() is true."],"exampleFix":"// before\nvar list = await readingListService.CreateReadingListForUser(user, title);\n\n// after\nif (string.IsNullOrWhiteSpace(title))\n    return BadRequest(\"Title is required\");\nvar list = await readingListService.CreateReadingListForUser(user, title.Trim());","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(title))\n    return BadRequest(\"Title is required\");\nvar list = await readingListService.CreateReadingListForUser(user, title.Trim());","typeGuard":"static bool IsValidListTitle(string? title) => !string.IsNullOrWhiteSpace(title);","tryCatchPattern":null,"preventionTips":["Require non-empty title in the create form.","Ensure the AppUser is tracked by the same unitOfWork.","Verify ReadingListBuilder.Build sets Title so HasChanges is true."],"tags":["reading-lists","validation","data-integrity","builder"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}