signalapp/Signal-Server · error · IllegalArgumentException

cannot delete primary device

Error message

cannot delete primary device

What it means

UnlinkDeviceCommand refuses to remove a device whose ID equals Device.PRIMARY_ID. The primary device (ID 1) anchors the account; deleting it via the unlink command is unsupported, so run() throws IllegalArgumentException before calling removeDevice.

Solutions

  1. Remove only linked (non-primary) device IDs, i.e. exclude 1 from --deviceIds
  2. To delete the entire account use the account-deletion path instead of unlink-device
  3. List the account's devices first and pick the linked device ID to remove

Example fix

// before
unlink-device --uuid <aci> --deviceIds 1,2
// after
unlink-device --uuid <aci> --deviceIds 2
Defensive patterns

Strategy: validation

Validate before calling

List<Byte> deviceIds = namespace.getList("deviceIds");
if (deviceIds.contains(Device.PRIMARY_ID)) {
    throw new IllegalArgumentException("primary device cannot be unlinked");
}

Try / catch

try { run(env, cfg, deps); } catch (IllegalArgumentException e) { System.out.println("refused: " + e.getMessage()); }

Prevention

When it happens

Trigger: Running `unlink-device --uuid <aci> --deviceIds 1` (or a list containing 1), where Device.PRIMARY_ID == 1.

Common situations: Operator intending to wipe an account tries to delete device 1; scripted device cleanup iterating all devices including the primary; misunderstanding that unlinking the primary deletes the whole account.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/2487ead6c73baed1. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/workers/UnlinkDeviceCommand.java:56

        .action(Arguments.append())
        .required(true);

    subparser.addArgument("-u", "--uuid")
        .help("the UUID of the account to modify")
        .dest("uuid")
        .type(String.class)
        .required(true);
  }

  @Override
  protected void run(final Environment environment, final Namespace namespace,
      final WhisperServerConfiguration configuration,
      final CommandDependencies deps) throws Exception {
    final UUID aci = UUID.fromString(namespace.getString("uuid").trim());
    final List<Byte> deviceIds = namespace.getList("deviceIds");

    if (deviceIds.contains(Device.PRIMARY_ID)) {
      throw new IllegalArgumentException("cannot delete primary device");
    }

    for (byte deviceId : deviceIds) {
      /** see {@link org.whispersystems.textsecuregcm.controllers.DeviceController#removeDevice} */
      System.out.format("Removing device %s::%d\n", aci, deviceId);
      deps.accountsManager().removeDevice(aci, deviceId);
    }
  }
}

View on GitHub (pinned to 100ab61c82)