signalapp/Signal-Server · error · BraintreeException

result.getMessage()

Error message

result.getMessage()

What it means

BraintreeManager.createCustomer calls braintreeGateway.customer().create(request); when the Braintree gateway returns an unsuccessful Result (validation errors, declined, gateway problems), Signal wraps result.getMessage() in a BraintreeException. The message text is whatever Braintree's SDK reported, so the underlying cause is a rejected Braintree customer-creation request.

Solutions

  1. Inspect result.getMessage() / result.getErrors() in logs to get Braintree's specific validation error and fix the corresponding field.
  2. Verify Braintree gateway credentials (merchantId, publicKey, privateKey) and that environment matches the key set (SANDBOX vs PRODUCTION).
  3. If a duplicate customer id is being sent, generate a unique customer id or look up the existing customer first.
  4. Retry with backoff if Braintree reports a transient gateway/system error.

Example fix

// before
Result<Customer> result = braintreeGateway.customer().create(request);
if (!result.isSuccess()) {
  throw new BraintreeException(result.getMessage());
}
// after: surface detailed errors
if (!result.isSuccess()) {
  logger.warn("Braintree customer create failed: {}", result.getErrors().getAllDeepValidationErrors());
  throw new BraintreeException(result.getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify credentials and environment at startup
braintreeGateway.clientToken().generate(); // fails fast on bad credentials/environment

Try / catch

try {
  processorCustomer = braintreeManager.createCustomer(...);
} catch (BraintreeException e) {
  log.warn("Braintree customer create failed: {}", e.getMessage());
  showPaymentProviderUnavailable();
}

Prevention

When it happens

Trigger: braintreeGateway.customer().create(request) returns isSuccess() == false — e.g. invalid merchant credentials, processor/AVS validation errors, duplicate customer id, or Braintree API outage.

Common situations: Misconfigured Braintree environment (sandbox keys used against production or vice versa), expired/rotated API keys, invalid customer fields (bad id format, too-long custom fields), Braintree transient outage during signup.

Related errors


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/subscriptions/BraintreeManager.java:336

  }

  public record PayPalChargeSuccessDetails(String paymentId) {

  }

  @Override
  public ProcessorCustomer createCustomer(final byte[] subscriberUser, @Nullable final ClientPlatform clientPlatform) {
    final CustomerRequest request = new CustomerRequest()
        .customField("subscriber_user", HexFormat.of().formatHex(subscriberUser));

    if (clientPlatform != null) {
      request.customField("client_platform", clientPlatform.name().toLowerCase());
    }

    final Result<Customer> result = braintreeGateway.customer().create(request);
    if (!result.isSuccess()) {
      throw new BraintreeException(result.getMessage());
    }
    return new ProcessorCustomer(result.getTarget().getId(), PaymentProvider.BRAINTREE);
  }

  @Override
  public String createPaymentMethodSetupToken(final String customerId) {
    final ClientTokenRequest request = new ClientTokenRequest().customerId(customerId);

    return braintreeGateway.clientToken().generate(request);
  }

  @Override
  public void setDefaultPaymentMethodForCustomer(final String customerId, final String billingAgreementToken,
      @Nullable final String currentSubscriptionId) throws IOException {
    final Optional<String> maybeSubscriptionId = Optional.ofNullable(currentSubscriptionId);
    final TokenizePayPalBillingAgreementMutation.TokenizePayPalBillingAgreement tokenizePayPalBillingAgreement =
        braintreeGraphqlClient.tokenizePayPalBillingAgreement(billingAgreementToken);
    final VaultPaymentMethodMutation.VaultPaymentMethod vaultPaymentMethod =

View on GitHub (pinned to 100ab61c82)