grpc/grpc-java · error · S2AConnectionException
No valid response received from S2A.
Error message
No valid response received from S2A.
What it means
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.
Source
Thrown at s2a/src/main/java/io/grpc/s2a/internal/handshaker/S2APrivateKeyMethod.java:138
.setOffloadPrivateKeyOperationReq(
OffloadPrivateKeyOperationReq.newBuilder()
.setOperation(OffloadPrivateKeyOperationReq.PrivateKeyOperation.SIGN)
.setSignatureAlgorithm(s2aSignatureAlgorithm)
.setRawBytes(ByteString.copyFrom(input)));
if (localIdentity.isPresent()) {
reqBuilder.setLocalIdentity(localIdentity.get().getIdentity());
}
SessionResp resp = stub.send(reqBuilder.build());
if (resp.hasStatus() && resp.getStatus().getCode() != 0) {
throw new S2AConnectionException(
String.format(
"Error occurred in response from S2A, error code: %d, error message: \"%s\".",
resp.getStatus().getCode(), resp.getStatus().getDetails()));
}
if (!resp.hasOffloadPrivateKeyOperationResp()) {
throw new S2AConnectionException("No valid response received from S2A.");
}
return resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray();
}
@Override
public byte[] decrypt(SSLEngine engine, byte[] input) {
throw new UnsupportedOperationException("decrypt is not supported.");
}
}View on GitHub (pinned to 64daddc1f3)
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.
Example fix
// before
byte[] out = resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray();
// after
if (!resp.hasOffloadPrivateKeyOperationResp()) {
throw new IllegalStateException("S2A returned no offload response");
}
byte[] out = resp.getOffloadPrivateKeyOperationResp().getOutBytes().toByteArray(); Defensive patterns
Strategy: try-catch
Try / catch
try {
byte[] sig = keyMethod.sign(engine, input);
} catch (S2AConnectionException e) {
if ("No valid response received from S2A.".equals(e.getMessage())) {
retryOnceThenFail(e); // likely proto/version skew or server bug
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- TLS version %d is not supported.
- Signature Algorithm %d is not supported.
- Error occurred in response from S2A, error code: %d, error m
- decrypt is not supported.
- Received an unexpected response from a host at the S2A's add
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/f44431501e774e3d.
Report an issue: GitHub.