{"record":{"id":"76af921b8aedd654","repo":"Kareadita/Kavita","slug":"device-duplicate","errorCode":null,"errorMessage":"device-duplicate","messagePattern":"device-duplicate","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"warning","filePath":"Kavita.Services/DeviceService.cs","lineNumber":34,"sourceCode":"using Kavita.Models.Entities.User;\nusing Microsoft.Extensions.Logging;\n\nnamespace Kavita.Services;\n\npublic class DeviceService(\n    IUnitOfWork unitOfWork,\n    ILogger<DeviceService> logger,\n    IEmailService emailService,\n    IReadingProfileService readingProfileService)\n    : IDeviceService\n{\n    public async Task<Device?> Create(CreateEmailDeviceDto dto, AppUser userWithDevices, CancellationToken ct = default)\n    {\n        try\n        {\n            userWithDevices.Devices ??= new List<Device>();\n            var existingDevice = userWithDevices.Devices.SingleOrDefault(d => d.Name!.Equals(dto.Name));\n            if (existingDevice != null) throw new KavitaException(\"device-duplicate\");\n\n            existingDevice = new DeviceBuilder(dto.Name)\n                .WithPlatform(dto.Platform)\n                .WithEmail(dto.EmailAddress)\n                .Build();\n\n\n            userWithDevices.Devices.Add(existingDevice);\n            unitOfWork.UserRepository.Update(userWithDevices);\n\n            if (!unitOfWork.HasChanges()) return existingDevice;\n            if (await unitOfWork.CommitAsync(ct)) return existingDevice;\n        }\n        catch (Exception ex)\n        {\n            logger.LogError(ex, \"There was an error when creating your device\");\n            await unitOfWork.RollbackAsync(ct);\n        }","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/DeviceService.cs#L16-L52","documentation":"Thrown by DeviceService.Create when a device with the same name already exists in the user's device list. The check 'userWithDevices.Devices.SingleOrDefault(d => d.Name!.Equals(dto.Name))' performs a case-sensitive exact match on the device name. Device names must be unique per user.","triggerScenarios":"User creates a new email device with a name identical to an existing one; the UI did not refresh the device list after a previous creation; user re-submits a form after a double-click.","commonSituations":"Double-submit from the client creating the same device twice; user forgets they already set up a device with that name; importing or restoring a device configuration that conflicts with existing names.","solutions":["Choose a different, unique name for the new device","Refresh the device list in the UI before creating to see current names","If the existing device is the same one, update it instead of creating a new one","Add client-side duplicate name checking before form submission"],"exampleFix":"// Client-side guard:\n// const existing = devices.value.find(d => d.name === dto.name);\n// if (existing) { toast.error('A device with this name already exists'); return; }","handlingStrategy":"validation","validationCode":"// Check for existing device name before creation:\n// var existing = userWithDevices.Devices?.FirstOrDefault(d => d.Name!.Equals(dto.Name));\n// if (existing != null) {\n//     return BadRequest(\"A device with this name already exists\");\n// }","typeGuard":null,"tryCatchPattern":"// try {\n//     var device = await deviceService.Create(dto, userWithDevices, ct);\n//     return Ok(device);\n// } catch (KavitaException ex) when (ex.Message == \"device-duplicate\") {\n//     return BadRequest(new { error = \"A device with this name already exists.\" });\n// }","preventionTips":["Refresh the device list before creating new devices to see current names","Implement client-side real-time duplicate name checking","Disable the create button on duplicate detection to prevent submission"],"tags":["device","duplicate","create","unique-constraint"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}