grpc/grpc-java · error · CertificateException

No valid response received from S2A.

Error message

No valid response received from S2A.

What it means

If the S2A's SessionResp contains neither a status error nor a ValidatePeerCertificateChainResp, S2ATrustManager.checkPeerTrusted() throws 'No valid response received from S2A.'. The response shape is a protocol invariant; a response without the validation payload indicates a broken or incompatible S2A.

Source

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

    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 {
    ImmutableList.Builder<ByteString> derChain = ImmutableList.<ByteString>builder();
    for (X509Certificate certificate : chain) {
      derChain.add(ByteString.copyFrom(certificate.getEncoded()));
    }
    return derChain.build();
  }
}

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Verify the S2A process version matches the client library's handshaker proto version.
  2. Confirm the address serves the actual S2A service, not another gRPC server.
  3. Upgrade the grpc-s2a client library and the S2A to compatible releases.

Example fix

// before
// client uses older proto than S2A binary
// after
// align versions: client handshaker proto vX with S2A vX
s2aProcess.start("s2a:vX"); // same version family as the client library
Defensive patterns

Strategy: validation

Validate before calling

// verify proto version compatibility at startup
if (!handshakerProtoVersionMatches(s2aProcess.getVersion(), clientProtoVersion)) {
  throw new IllegalStateException("S2A handshaker proto version mismatch");
}

Try / catch

try {
  resp = stub.send(req);
} catch (IOException e) {
  // validate resp shape defensively before use
  if (!resp.hasStatus() && !resp.hasValidatePeerCertificateChainResp()) {
    throw new IllegalStateException("Malformed SessionResp from S2A");
  }
}

Prevention

When it happens

Trigger: checkClientTrusted/checkServerTrusted gets a SessionResp with no status and no validate_peer_certificate_chain_resp field set — e.g. the S2A answered a different request type or an incompatible proto version.

Common situations: Proto version mismatch between the client library and the S2A binary; a non-S2A gRPC server responding at the configured address; S2A bug producing a malformed response.

Related errors


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