grpc/grpc-java · error · CertificateException

Error occurred in response from S2A, error code: %d, error m

Error message

Error occurred in response from S2A, error code: %d, error message: %s.

What it means

When the SessionResp from the S2A carries a non-zero status code, S2ATrustManager.checkPeerTrusted() throws a CertificateException formatted with the S2A-provided error code and message. This means the S2A itself reported an error processing the validation request, as opposed to a transport failure.

Source

Thrown at s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2ATrustManager.java:160

    }

    SessionReq.Builder reqBuilder =
        SessionReq.newBuilder().setValidatePeerCertificateChainReq(validatePeerCertificateChainReq);
    if (localIdentity.isPresent()) {
      reqBuilder.setLocalIdentity(localIdentity.get().getIdentity());
    }

    SessionResp resp;
    try {
      resp = stub.send(reqBuilder.build());
    } catch (IOException e) {
      throw new CertificateException("Failed to send request to S2A.", e);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new CertificateException("Failed to send request to S2A.", e);
    }
    if (resp.hasStatus() && resp.getStatus().getCode() != 0) {
      throw new CertificateException(
          String.format(
              "Error occurred in response from S2A, error code: %d, error message: %s.",
              resp.getStatus().getCode(), resp.getStatus().getDetails()));
    }

    if (!resp.hasValidatePeerCertificateChainResp()) {
      throw new CertificateException("No valid response received from S2A.");
    }

    ValidatePeerCertificateChainResp validationResult = resp.getValidatePeerCertificateChainResp();
    if (validationResult.getValidationResult()
        != ValidatePeerCertificateChainResp.ValidationResult.SUCCESS) {
      throw new CertificateException(validationResult.getValidationDetails());
    }
  }

  private static ImmutableList<ByteString> certificateChainToDerChain(X509Certificate[] chain)
      throws CertificateEncodingException {

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Read the S2A error code/message in the exception and map it to the S2A-side issue.
  2. Verify the S2A is configured for the local identity and peer trust domains in use.
  3. Check client/S2A proto version compatibility and upgrade accordingly.

Example fix

// before
// ignore S2A status, unclear why validation fails
// after
catch (CertificateException e) {
  logger.severe("S2A reported error: " + e.getMessage()); // inspect code + details
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
  if (e.getMessage().startsWith("Error occurred in response from S2A")) {
    // parse embedded S2A error code/details for diagnosis
    logger.severe("S2A-side error: " + e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: checkClientTrusted/checkServerTrusted receives a SessionResp where resp.hasStatus() is true and resp.getStatus().getCode() != 0 — the S2A rejected or failed the request server-side.

Common situations: S2A not configured with the needed identity/key material; S2A internal error while validating the peer chain; version mismatch between client and S2A protobuf API.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/02afe0bf331c77c9. Report an issue: GitHub.