signalapp/Signal-Server · error · BadRequestException
login purchases are not enabled
Error message
login purchases are not enabled
What it means
LoginPurchaseController.createLoginReceiptCredential is gated by a server-side dynamic configuration flag, loginPurchaseConfiguration.enabled(). When the flag is off, the endpoint throws BadRequestException('login purchases are not enabled') before processing anything else. It is an intentional feature-gate, not a client bug.
Solutions
- Enable login purchases in the server's dynamic configuration (set loginPurchaseConfiguration.enabled to true and reload)
- Wait for the operator to enable the feature flag if you don't control the server
- Confirm you are pointing at the environment where the feature is enabled
- Handle the 400 client-side by disabling login-purchase UI when the server rejects it
Example fix
# server dynamic configuration
// before
"loginPurchase": { "enabled": false }
// after
"loginPurchase": { "enabled": true } # then reload dynamic configuration Defensive patterns
Strategy: try-catch
Try / catch
try {
client.createLoginReceiptCredential(request);
} catch (WebApplicationException e) {
if (e.getResponse().getStatus() == 400 && String.valueOf(e.getResponse().getEntity()).contains("not enabled")) {
// hide login-purchase UI; feature disabled server-side
} else throw e;
} Prevention
- Check feature availability before showing login-purchase UI
- Confirm the environment has the flag enabled before testing
- Coordinate client releases with server feature rollouts
When it happens
Trigger: POST to the login purchase receipt-credential endpoint while the server's DynamicConfiguration loginPurchaseConfiguration.enabled() is false.
Common situations: Staging/self-hosted servers without the feature flag enabled in dynamic configuration; testing before an ops rollout; clients built against a server where the feature was temporarily disabled.
Related errors
- invalid captcha scheme
- invalid captcha site-key
- Endpoint requires unauthenticated access
- login purchases are not enabled
- target-queue-size-bytes must be positive
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/97b7759184a7b217.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/LoginPurchaseController.java:108
""")
@ApiResponse(responseCode = "402", description = "The purchase did not complete successfully. The body may include ChargeFailure details.",
content = @Content(schema = @Schema(
nullable = true,
implementation = SubscriptionExceptionMapper.ChargeFailureResponse.class)))
@ApiResponse(responseCode = "403", description = "The request was made on an authenticated channel")
@ApiResponse(responseCode = "404", description = "The payment provider has no purchase with the provided purchaseIdentifier")
@ApiResponse(responseCode = "409", description = "The purchase was already redeemed for a receipt credential, but with a different receipt credential request")
@ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header(
name = "Retry-After",
description = "If present, a positive integer indicating the number of seconds before a subsequent attempt could succeed"))
@ManagedAsync
public Response createLoginReceiptCredential(
@Auth final Optional<AuthenticatedDevice> authenticatedAccount,
@NotNull @Valid final CreateLoginReceiptCredentialRequest request)
throws IOException, SubscriptionPaymentRequiredException, SubscriptionInvalidArgumentsException, SubscriptionNotFoundException, RateLimitExceededException, SubscriptionReceiptAlreadyRedeemedException {
if (!dynamicConfigurationManager.getConfiguration().getLoginPurchaseConfiguration().enabled()) {
throw new BadRequestException("login purchases are not enabled");
}
if (authenticatedAccount.isPresent()) {
throw new ForbiddenException("must not use authenticated connection for login purchase operations");
}
final ReceiptCredentialRequest receiptCredentialRequest;
try {
receiptCredentialRequest = new ReceiptCredentialRequest(request.receiptCredentialRequest);
} catch (final InvalidInputException e) {
throw new BadRequestException("invalid receipt credential request", e);
}
try {
final ReceiptCredentialResponse receiptCredentialResponse = loginPurchaseManager.generateReceipt(
request.paymentProvider, request.purchaseIdentifier, receiptCredentialRequest);
return Response.ok(
new CreateLoginReceiptCredentialResponse(receiptCredentialResponse.serialize()))View on GitHub (pinned to 100ab61c82)