Kareadita/Kavita · error · KavitaException

client-device-doesnt-exist

Error message

client-device-doesnt-exist

What it means

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).

Source

Thrown at Kavita.Services/ClientDeviceService.cs:113

            return false;
        }

        device.FriendlyName = newName;
        await unitOfWork.CommitAsync(ct);

        logger.LogInformation("User {UserId} renamed device {DeviceId} to '{Name}'",
            userId, deviceId, newName);

        return true;
    }

    public async Task<bool> DeleteDeviceAsync(int userId, int deviceId, CancellationToken ct = default)
    {
        var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId, ct);

        if (device == null)
        {
            throw new KavitaException("client-device-doesnt-exist");
        }

        device.IsActive = false;
        await unitOfWork.CommitAsync(ct);

        logger.LogInformation("User {UserId} removed device {DeviceId}", userId, deviceId);

        return true;
    }


    public async Task UpdateFriendlyNameAsync(int userId, UpdateClientDeviceNameDto dto, CancellationToken ct = default)
    {
        var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(dto.DeviceId, userId, ct)
                     ?? throw new KavitaException("client-device-doesnt-exist");

        if (!string.IsNullOrWhiteSpace(dto.Name))
        {

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Refresh the device list in the UI before retrying the delete
  2. Verify the device ID in the request matches an active device returned by the list-devices endpoint
  3. Handle the KavitaException in the controller and return a 404 or user-friendly 'device not found' message

Example fix

// Controller-level guard before calling the service:
// var exists = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId);
// if (exists == null) return NotFound("Device not found");
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify device exists before deletion:
// var device = await unitOfWork.ClientDeviceRepository.GetClientDeviceById(deviceId, userId, ct);
// if (device == null) return NotFound("Device not found or already removed");

Try / catch

// try {
//     await clientDeviceService.DeleteDeviceAsync(userId, deviceId, ct);
//     return Ok();
// } catch (KavitaException ex) when (ex.Message == "client-device-doesnt-exist") {
//     return NotFound(new { error = "This device has already been removed." });
// }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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