signalapp/Signal-Server · warning · BadRequestException
login purchases are not enabled
Error message
login purchases are not enabled
What it means
registerAccountWithoutNumber (called from the /v1/registration flow in RegistrationController) rejects login-purchase registrations when the feature is switched off. The controller checks DynamicConfigurationManager's LoginPurchaseConfiguration.enabled() and throws a BadRequestException (HTTP 400) immediately if the server has not enabled the feature. It is a server-side feature gate, not a client error.
Solutions
- Enable the feature in the server's dynamic configuration: set loginPurchaseConfiguration.enabled() to true and allow the config to propagate to the server.
- Verify the deployed dynamic configuration actually contains the loginPurchaseConfiguration block and that the server picked it up (check config file and reload).
- If you are a client developer, treat HTTP 400 'login purchases are not enabled' as feature-disabled and fall back to the normal (non-purchase) registration path.
- Confirm you are targeting the intended server deployment; a staging server with the flag off will reject requests meant for production.
Example fix
// server config (before) loginPurchase: enabled: false // after loginPurchase: enabled: true
Defensive patterns
Strategy: fallback
Try / catch
try {
registerWithoutNumber(request);
} catch (BadRequestException e) {
if ("login purchases are not enabled".equals(e.getMessage())) {
fallBackToStandardRegistration(request); // feature-off path
} else throw e;
} Prevention
- Query a feature/capability flag from the server before offering the login-purchase UI.
- Keep server dynamic configuration and client releases coordinated.
- Monitor for this 400 in staging to catch missing config blocks early.
When it happens
Trigger: A client POSTs to the registration endpoint with a receipt credential / login-purchase flow while the server operator's dynamic configuration has loginPurchaseConfiguration.enabled() set to false (or the config field is absent/default-disabled).
Common situations: Self-hosted or staging Signal servers where the login-purchase feature flag was never enabled; environments where the dynamic configuration YAML lacks the loginPurchase section (so defaults to disabled); production operators disabling the feature without coordinating with client releases that rely on it.
Related errors
- Invalid receipt credential presentation
- Invalid receipt level
- number does not match session
- account does not have a phone number
- Operation requires unauthenticated access
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/b8e5b70d70ab38ab.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java:334
final AccountIdentityResponse identityResponse = new AccountIdentityResponseBuilder(account)
// If there was an existing account, return whether it could have had something in the storage service
.storageCapable(existingAccount
.map(a -> a.hasCapability(DeviceCapability.STORAGE))
.orElse(false))
.build();
return new AccountCreationResponse(identityResponse, existingAccount.isPresent());
}
private AccountCreationResponse registerAccountWithoutNumber(
final String password,
final RegistrationRequest registrationRequest,
final String userAgent,
final String signalAgent) {
if (!dynamicConfigurationManager.getConfiguration().getLoginPurchaseConfiguration().enabled()) {
throw new BadRequestException("login purchases are not enabled");
}
registrationRequest.accountAttributes().recoveryPassword()
.filter(ArrayUtils::isNotEmpty)
.orElseThrow(() -> new WebApplicationException("Account recovery password is required", 422));
final ReceiptCredentialPresentation receiptCredentialPresentation;
try {
receiptCredentialPresentation = receiptCredentialPresentationFactory
.build(registrationRequest.receiptCredentialPresentation());
} catch (InvalidInputException _) {
throw new BadRequestException("Invalid receipt credential presentation");
}
try {
serverZkReceiptOperations.verifyReceiptCredentialPresentation(receiptCredentialPresentation);
} catch (VerificationFailedException _) {
throw new NotAuthorizedException("Receipt credential presentation verification failed");
}View on GitHub (pinned to 100ab61c82)