{"record":{"id":"9f38b51f251c28c8","repo":"Kareadita/Kavita","slug":"client-device-doesnt-exist","errorCode":null,"errorMessage":"client-device-doesnt-exist","messagePattern":"client-device-doesnt-exist","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"error","filePath":"Kavita.Services/ClientDeviceService.cs","lineNumber":113,"sourceCode":"            return false;\n        }\n\n        device.FriendlyName = newName;\n        await unitOfWork.CommitAsync(ct);\n\n        logger.LogInformation(\"User {UserId} renamed device {DeviceId} to '{Name}'\",\n            userId, deviceId, newName);\n\n        return true;\n    }\n\n    public async Task<bool> DeleteDeviceAsync(int userId, int deviceId, CancellationToken ct = default)\n    {\n        var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId, ct);\n\n        if (device == null)\n        {\n            throw new KavitaException(\"client-device-doesnt-exist\");\n        }\n\n        device.IsActive = false;\n        await unitOfWork.CommitAsync(ct);\n\n        logger.LogInformation(\"User {UserId} removed device {DeviceId}\", userId, deviceId);\n\n        return true;\n    }\n\n\n    public async Task UpdateFriendlyNameAsync(int userId, UpdateClientDeviceNameDto dto, CancellationToken ct = default)\n    {\n        var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(dto.DeviceId, userId, ct)\n                     ?? throw new KavitaException(\"client-device-doesnt-exist\");\n\n        if (!string.IsNullOrWhiteSpace(dto.Name))\n        {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/ClientDeviceService.cs#L95-L131","documentation":"Thrown by DeleteDeviceAsync when the specified deviceId is not found for the given userId. The repository call GetClientDeviceById(deviceId, userId) scopes the lookup to the user, so this fires whether the device ID is wrong or belongs to another user. It is a KavitaException (UI-facing, not Sentry).","triggerScenarios":"User attempts to delete a client device whose ID was already removed; user sends a delete request with a stale device ID from a cached UI; the device ID was tampered with in the request.","commonSituations":"The UI's device list is stale after another session deleted a device; race condition where two tabs attempt to delete the same device; client-side caching of device IDs across sessions.","solutions":["Refresh the device list in the UI before retrying the delete","Verify the device ID in the request matches an active device returned by the list-devices endpoint","Handle the KavitaException in the controller and return a 404 or user-friendly 'device not found' message"],"exampleFix":"// Controller-level guard before calling the service:\n// var exists = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId);\n// if (exists == null) return NotFound(\"Device not found\");","handlingStrategy":"try-catch","validationCode":"// Verify device exists before deletion:\n// var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId, ct);\n// if (device == null) return NotFound(\"Device not found or already removed\");","typeGuard":null,"tryCatchPattern":"// try {\n//     await clientDeviceService.DeleteDeviceAsync(userId, deviceId, ct);\n//     return Ok();\n// } catch (KavitaException ex) when (ex.Message == \"client-device-doesnt-exist\") {\n//     return NotFound(new { error = \"This device has already been removed.\" });\n// }","preventionTips":["Refresh the device list in the UI before performing delete operations","Handle stale device IDs gracefully with a user-friendly message","Use optimistic concurrency checks if concurrent device management is expected"],"tags":["client-device","not-found","delete","user-scoped"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}