signalapp/Signal-Server · error · SubscriptionInvalidArgumentsException

receipt credential request failed verification

Error message

receipt credential request failed verification

What it means

createReceiptCredentials throws SubscriptionInvalidArgumentsException("receipt credential request failed verification") when the zero-knowledge receipt issuance step (issueReceiptCredential or recordIssuance) raises VerificationFailedException. The request parsed fine but its cryptographic proof did not verify against the server's receipt credential public parameters/issuer key.

Solutions

  1. Regenerate the ReceiptCredentialRequest with the latest client library so it is built against the server's current receipt credential parameters.
  2. Confirm server zk ReceiptCredentialOperations issuer keys are unchanged/consistently configured; if keys were rotated, redeploy clients with the new public parameters.
  3. Check client and server use compatible versions of the receipts/zk library; upgrade whichever is stale.

Example fix

// before
ReceiptCredentialRequest req = new ReceiptCredentialRequest(legacySerialized); // built with old params
// after
ReceiptCredentialRequest req = ReceiptCredentialRequestFactory.create(currentParams, receiptLevel, expiration);
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: ensure request built against the server's current public parameters
if (!paramsVersion.equals(SERVER_CURRENT_PARAMS_VERSION)) {
  refreshReceiptParameters();
}

Try / catch

try {
  manager.createReceiptCredentials(creds, requestBytes);
} catch (SubscriptionInvalidArgumentsException e) {
  // verification failed: rebuild request with current zk parameters and retry once
} catch (SubscriptionReceiptAlreadyRedeemedException e) {
  // distinct failure: request already redeemed
}

Prevention

When it happens

Trigger: Calling createReceiptCredentials where zkReceiptOperations.issueReceiptCredential rejects the ReceiptCredentialRequest proof, or recordIssuance fails verification, for a given subscription record and receipt level.

Common situations: Server rotated its ZK issuer key/parameters while the client built the request against old parameters; client and server zk library versions disagree on the proof format; a crafted/replayed request from a non-genuine client.

Related errors


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/storage/SubscriptionManager.java:223

      receiptCredentialRequest = new ReceiptCredentialRequest(receiptCredentialRequestBytes);
    } catch (final InvalidInputException e) {
      throw new SubscriptionInvalidArgumentsException("invalid receipt credential request", e);
    }

    final PaymentProvider processor = record.getProcessorCustomer().orElseThrow().processor();
    final SubscriptionPaymentProcessor manager = getProcessor(processor);
    final SubscriptionPaymentProcessor.ReceiptItem receipt = manager.getReceiptItem(record.subscriptionId);
    final Instant expirationInstant = expiration.apply(receipt);
    final ReceiptCredentialResponse receiptCredentialResponse;
    try {
      issuedReceiptsManager
          .recordIssuance(receipt.itemId(), manager.getProvider(), receiptCredentialRequest, expirationInstant);
      receiptCredentialResponse = zkReceiptOperations.issueReceiptCredential(
          receiptCredentialRequest,
          expirationInstant.getEpochSecond(),
          receipt.level());
    } catch (final VerificationFailedException e) {
      throw new SubscriptionInvalidArgumentsException("receipt credential request failed verification", e);
    } catch (final WriteConflictException _) {
      throw new SubscriptionReceiptAlreadyRedeemedException();
    }
    return new ReceiptResult(receiptCredentialResponse, receipt, processor);
  }

  /**
   * Add a payment method to a customer in a payment processor and update the table.
   * <p>
   * If the customer does not exist in the table, a customer is created via the subscriptionPaymentProcessor and added
   * to the table. Not all payment processors support server-managed customers, so a payment processor that implements
   * {@link CustomerAwareSubscriptionPaymentProcessor} must be passed in.
   *
   * @param subscriberCredentials        Subscriber credentials derived from the subscriberId
   * @param subscriptionPaymentProcessor A customer-aware payment processor to use. If the subscriber already has a
   *                                     payment processor, it must match the existing one.
   * @param clientPlatform               The platform of the client making the request
   * @param paymentSetupFunction         A function that takes the payment processor and the customer ID and begins

View on GitHub (pinned to 100ab61c82)