{"record":{"id":"2487ead6c73baed1","repo":"signalapp/Signal-Server","slug":"cannot-delete-primary-device","errorCode":null,"errorMessage":"cannot delete primary device","messagePattern":"cannot delete primary device","errorType":"console","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/workers/UnlinkDeviceCommand.java","lineNumber":56,"sourceCode":"        .action(Arguments.append())\n        .required(true);\n\n    subparser.addArgument(\"-u\", \"--uuid\")\n        .help(\"the UUID of the account to modify\")\n        .dest(\"uuid\")\n        .type(String.class)\n        .required(true);\n  }\n\n  @Override\n  protected void run(final Environment environment, final Namespace namespace,\n      final WhisperServerConfiguration configuration,\n      final CommandDependencies deps) throws Exception {\n    final UUID aci = UUID.fromString(namespace.getString(\"uuid\").trim());\n    final List<Byte> deviceIds = namespace.getList(\"deviceIds\");\n\n    if (deviceIds.contains(Device.PRIMARY_ID)) {\n      throw new IllegalArgumentException(\"cannot delete primary device\");\n    }\n\n    for (byte deviceId : deviceIds) {\n      /** see {@link org.whispersystems.textsecuregcm.controllers.DeviceController#removeDevice} */\n      System.out.format(\"Removing device %s::%d\\n\", aci, deviceId);\n      deps.accountsManager().removeDevice(aci, deviceId);\n    }\n  }\n}\n","sourceCodeStart":38,"sourceCodeEnd":66,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/workers/UnlinkDeviceCommand.java#L38-L66","documentation":"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.","triggerScenarios":"Running `unlink-device --uuid <aci> --deviceIds 1` (or a list containing 1), where Device.PRIMARY_ID == 1.","commonSituations":"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.","solutions":["Remove only linked (non-primary) device IDs, i.e. exclude 1 from --deviceIds","To delete the entire account use the account-deletion path instead of unlink-device","List the account's devices first and pick the linked device ID to remove"],"exampleFix":"// before\nunlink-device --uuid <aci> --deviceIds 1,2\n// after\nunlink-device --uuid <aci> --deviceIds 2","handlingStrategy":"validation","validationCode":"List<Byte> deviceIds = namespace.getList(\"deviceIds\");\nif (deviceIds.contains(Device.PRIMARY_ID)) {\n    throw new IllegalArgumentException(\"primary device cannot be unlinked\");\n}","typeGuard":null,"tryCatchPattern":"try { run(env, cfg, deps); } catch (IllegalArgumentException e) { System.out.println(\"refused: \" + e.getMessage()); }","preventionTips":["Filter out primary ID 1 before passing deviceIds","Use account deletion for whole-account removal","Enumerate devices first and unlink only linked devices"],"tags":["cli","validation","devices"],"backgroundTag":"unsupported-operation","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}