{"record":{"id":"965c722c6149b1ee","repo":"signalapp/Signal-Server","slug":"account-does-not-have-a-phone-number","errorCode":null,"errorMessage":"account does not have a phone number","messagePattern":"account does not have a phone number","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java","lineNumber":243,"sourceCode":"    final Collection<TransactWriteItem> additionalWriteItems =\n        account.getPhoneNumberIdentifier()\n            .flatMap(phoneNumberIdentifier -> attributes.recoveryPassword().map(recoveryPassword ->\n                List.of(phoneNumberRecoveryPasswordsManager.buildTransactWriteItemForStorePassword(phoneNumberIdentifier, recoveryPassword))))\n            .orElseGet(Collections::emptyList);\n\n    accounts.update(auth.accountIdentifier(), a -> {\n      a.getDevice(auth.deviceId()).ifPresent(d -> {\n        d.setFetchesMessages(attributes.getFetchesMessages());\n        d.setName(attributes.getName());\n        d.setLastSeen(Util.todayInMillis());\n        d.setCapabilities(attributes.getCapabilities());\n        if (StringUtils.isNotBlank(signalAgent)) {\n          d.setUserAgent(signalAgent);\n        }\n      });\n\n      if (StringUtils.isNotEmpty(attributes.getRegistrationLock()) && a.getNumber().isEmpty()) {\n        throw new BadRequestException(\"account does not have a phone number\");\n      }\n\n      a.setRegistrationLockFromAttributes(attributes);\n      a.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());\n      a.setUnrestrictedUnidentifiedAccess(attributes.isUnrestrictedUnidentifiedAccess());\n\n      if (attributes.isDiscoverableByPhoneNumber() && a.getNumber().isEmpty()) {\n        throw new BadRequestException(\"account does not have a phone number\");\n      }\n\n      a.setDiscoverableByPhoneNumber(attributes.isDiscoverableByPhoneNumber());\n\n      attributes.recoveryPassword().ifPresent(a::setAccountRecoveryPassword);\n    }, additionalWriteItems);\n  }\n\n  @GET\n  @Path(\"/whoami\")","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java#L225-L261","documentation":"In setAccountAttributes, if the request attempts to set a registration lock (RegistrationLock) on an account that has no phone number (e.g. a username-only or tokenized account), the server cannot associate the registration lock, so it rejects with BadRequestException('account does not have a phone number').","triggerScenarios":"PUT /v1/accounts/attributes with attributes containing a non-blank registrationLock while the authenticated account's number (a.getNumber()) is empty.","commonSituations":"Newer clients enabling registration lock on accounts created without a phone number, or after account migration to number-less identities where registration lock is unsupported.","solutions":["Remove the registrationLock field from the attributes request for accounts without a phone number.","Set up a phone number for the account before configuring a registration lock.","Update client logic to hide/disable registration-lock setup when the account has no phone number.","If server-side, gate the feature so number-less accounts skip registration lock handling."],"exampleFix":"// before\nattributes.setRegistrationLock(registrationLock); // account has no number\n// after\nif (account.getNumber().isPresent()) {\n  attributes.setRegistrationLock(registrationLock);\n}","handlingStrategy":"type-guard","validationCode":"const canSetRegistrationLock = (acct) => acct.number != null && acct.number !== '';","typeGuard":"const hasPhoneNumber = (a) => typeof a.number === 'string' && a.number.length > 0;","tryCatchPattern":"try { await setAttributes(attrs); } catch (e) { if (e.status === 400 && e.message.includes('does not have a phone number')) { delete attrs.registrationLock; return setAttributes(attrs); } throw e; }","preventionTips":["Check whether the account has a phone number before enabling registration lock","Disable registration-lock UI for number-less accounts","Keep client account state fresh before attribute updates"],"tags":["account","registration-lock","bad-request"],"backgroundTag":"invalid-state-transition","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"}