{"record":{"id":"648b976c9c553d2a","repo":"grpc/grpc-java","slug":"no-valid-response-received-from-s2a-648b97","errorCode":null,"errorMessage":"No valid response received from S2A.","messagePattern":"No valid response received from S2A\\.","errorType":"exception","errorClass":"CertificateException","httpStatus":null,"severity":"error","filePath":"s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2ATrustManager.java","lineNumber":167,"sourceCode":"\n    SessionResp resp;\n    try {\n      resp = stub.send(reqBuilder.build());\n    } catch (IOException e) {\n      throw new CertificateException(\"Failed to send request to S2A.\", e);\n    } catch (InterruptedException e) {\n      Thread.currentThread().interrupt();\n      throw new CertificateException(\"Failed to send request to S2A.\", e);\n    }\n    if (resp.hasStatus() && resp.getStatus().getCode() != 0) {\n      throw new CertificateException(\n          String.format(\n              \"Error occurred in response from S2A, error code: %d, error message: %s.\",\n              resp.getStatus().getCode(), resp.getStatus().getDetails()));\n    }\n\n    if (!resp.hasValidatePeerCertificateChainResp()) {\n      throw new CertificateException(\"No valid response received from S2A.\");\n    }\n\n    ValidatePeerCertificateChainResp validationResult = resp.getValidatePeerCertificateChainResp();\n    if (validationResult.getValidationResult()\n        != ValidatePeerCertificateChainResp.ValidationResult.SUCCESS) {\n      throw new CertificateException(validationResult.getValidationDetails());\n    }\n  }\n\n  private static ImmutableList<ByteString> certificateChainToDerChain(X509Certificate[] chain)\n      throws CertificateEncodingException {\n    ImmutableList.Builder<ByteString> derChain = ImmutableList.<ByteString>builder();\n    for (X509Certificate certificate : chain) {\n      derChain.add(ByteString.copyFrom(certificate.getEncoded()));\n    }\n    return derChain.build();\n  }\n}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2ATrustManager.java#L149-L185","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the S2A process version matches the client library's handshaker proto version.","Confirm the address serves the actual S2A service, not another gRPC server.","Upgrade the grpc-s2a client library and the S2A to compatible releases."],"exampleFix":"// before\n// client uses older proto than S2A binary\n// after\n// align versions: client handshaker proto vX with S2A vX\ns2aProcess.start(\"s2a:vX\"); // same version family as the client library","handlingStrategy":"validation","validationCode":"// verify proto version compatibility at startup\nif (!handshakerProtoVersionMatches(s2aProcess.getVersion(), clientProtoVersion)) {\n  throw new IllegalStateException(\"S2A handshaker proto version mismatch\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  resp = stub.send(req);\n} catch (IOException e) {\n  // validate resp shape defensively before use\n  if (!resp.hasStatus() && !resp.hasValidatePeerCertificateChainResp()) {\n    throw new IllegalStateException(\"Malformed SessionResp from S2A\");\n  }\n}","preventionTips":["Keep the S2A binary and client library on the same handshaker proto version.","Verify the configured endpoint serves the S2A service, not another gRPC server.","Upgrade both components together when protos change."],"tags":["tls","certificate-validation","s2a","protobuf"],"backgroundTag":"unexpected-response-shape","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}