signalapp/Signal-Server · error · BadRequestException

receipt credential request failed verification

Error message

receipt credential request failed verification

What it means

The receipt credential request parsed but failed zk verification (SubscriptionReceiptRequestedForOpenPaymentException aside, a VerificationFailedException is converted to this 400). The server rejected the cryptographic proof embedded in the request, so no receipt credential response is returned.

Solutions

  1. Regenerate the receipt credential request with the current libsignal version so the zk proof matches the server's verification key.
  2. Verify the client targets the correct environment (staging vs production server keys).
  3. Ensure the request bytes are not modified or truncated between client generation and server receipt.

Example fix

// before
// client sends stale request generated with old zk params -> server VerificationFailedException
// after
ReceiptCredentialRequest rcr = client.createReceiptCredentialRequest(newReceiptRequest(), serverParams.getCurrentVerificationKey());
sendToServer(rcr.serialize()); // fresh request verified against current server key
Defensive patterns

Strategy: retry

Validate before calling

// ensure the request was generated against the current server key
if (!receiptRequest.verifyAgainst(serverVerificationKey)) { regenerate before sending }

Try / catch

try { /* send */ } catch (BadRequestException e) { if (e.getMessage().contains("failed verification")) { regenerateReceiptRequest(); } }

Prevention

When it happens

Trigger: POST to the login receipt credential endpoint where loginPurchaseManager.generateReceipt(...) raises VerificationFailedException — i.e. the request's zk proof does not verify against the server's verification key.

Common situations: Client and server built with different zk parameters/keys (library version skew); receipt request constructed for a different server environment; corrupted proof bytes; replayed or tampered request.

Related errors


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/LoginPurchaseController.java:131

    }

    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()))
          .build();
    } catch (SubscriptionReceiptRequestedForOpenPaymentException e) {
      return Response.noContent().build();
    } catch (VerificationFailedException e) {
      throw new BadRequestException("receipt credential request failed verification", e);
    }
  }
}

View on GitHub (pinned to 100ab61c82)