{"record":{"id":"f44431501e774e3d","repo":"grpc/grpc-java","slug":"no-valid-response-received-from-s2a","errorCode":null,"errorMessage":"No valid response received from S2A.","messagePattern":"No valid response received from S2A\\.","errorType":"exception","errorClass":"S2AConnectionException","httpStatus":null,"severity":"error","filePath":"s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2APrivateKeyMethod.java","lineNumber":138,"sourceCode":"            .setOffloadPrivateKeyOperationReq(\n                OffloadPrivateKeyOperationReq.newBuilder()\n                    .setOperation(OffloadPrivateKeyOperationReq.PrivateKeyOperation.SIGN)\n                    .setSignatureAlgorithm(s2aSignatureAlgorithm)\n                    .setRawBytes(ByteString.copyFrom(input)));\n    if (localIdentity.isPresent()) {\n      reqBuilder.setLocalIdentity(localIdentity.get().getIdentity());\n    }\n\n    SessionResp resp = stub.send(reqBuilder.build());\n\n    if (resp.hasStatus() && resp.getStatus().getCode() != 0) {\n      throw new S2AConnectionException(\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    if (!resp.hasOffloadPrivateKeyOperationResp()) {\n      throw new S2AConnectionException(\"No valid response received from S2A.\");\n    }\n    return resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray();\n  }\n\n  @Override\n  public byte[] decrypt(SSLEngine engine, byte[] input) {\n    throw new UnsupportedOperationException(\"decrypt is not supported.\");\n  }\n}","sourceCodeStart":120,"sourceCodeEnd":147,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2APrivateKeyMethod.java#L120-L147","documentation":"After a successful (zero-status) S2A response, sign still requires the SessionResp to contain an offload_private_key_operation_resp payload. If the field is absent, S2AConnectionException('No valid response received from S2A.') is thrown — S2A answered, but not with the expected signing result.","triggerScenarios":"Calling sign(engine, bytes) where resp has status code 0 (or no status) but !resp.hasOffloadPrivateKeyOperationResp() — e.g. S2A returned an empty/default response, the response type is wrong (e.g. a session-resume or error variant), or request/response field mismatch due to proto version skew.","commonSituations":"S2A service bug or misrouting returning an empty SessionResp; handshaker proto version skew so the client reads a field the server never set; S2A instance replying before performing the operation; response corruption where hasStatus() is false and payload missing.","solutions":["Upgrade both the S2A service and gRPC s2a client to matching handshaker proto versions so the offload response field is populated consistently.","Check S2A server logs for the request; confirm the server actually performed the private-key operation.","Retry the operation once — if it recurs deterministically, treat it as an S2A service defect and report with the captured SessionResp shape.","Validate the response contract: assert hasOffloadPrivateKeyOperationResp() before consuming, so the failure is explicit and diagnosable."],"exampleFix":"// before\nbyte[] out = resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray();\n// after\nif (!resp.hasOffloadPrivateKeyOperationResp()) {\n  throw new IllegalStateException(\"S2A returned no offload response\");\n}\nbyte[] out = resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  byte[] sig = keyMethod.sign(engine, input);\n} catch (S2AConnectionException e) {\n  if (\"No valid response received from S2A.\".equals(e.getMessage())) {\n    retryOnceThenFail(e); // likely proto/version skew or server bug\n  } else {\n    throw e;\n  }\n}","preventionTips":["Match handshaker proto versions between client and S2A server.","Watch S2A server logs for requests that complete with empty responses.","Retry once on this error — it can be transient server behavior — but cap retries.","Report persistent occurrences to the S2A team with the response shape captured."],"tags":["grpc","s2a","tls","unexpected-response"],"backgroundTag":"unexpected-api-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"}