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
- Regenerate the receipt credential request with the current libsignal version so the zk proof matches the server's verification key.
- Verify the client targets the correct environment (staging vs production server keys).
- 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
- Keep client zk library version in sync with server expectations
- Target the correct environment's verification key (staging vs prod)
- Never mutate serialized request bytes after generation
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
- invalid receipt credential request
- Invalid length
- Invalid create call link credential request
- Group send endorsement tokens should not be combined with…
- Group send endorsement tokens should not be sent for story…
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)