grpc/grpc-java · error · GeneralSecurityException
TsiHandshakeHandler encountered exception
Error message
TsiHandshakeHandler encountered exception
What it means
TsiHandshakeHandler.sendHandshake() flushes queued TSI handshake frames through the TSI transport; if the underlying TsiTransport's write raises GeneralSecurityException, the handler wraps it in a new GeneralSecurityException labeled "TsiHandshakeHandler encountered exception", preserving the cause. This marks the ALTS/TSI handshake as failed at the Netty pipeline level.
Source
Thrown at alts/src/main/java/io/grpc/alts/internal/TsiHandshakeHandler.java:221
ctx.fireUserEventTriggered(localPne);
}
/** Sends as many bytes as are available from the handshaker to the remote peer. */
@SuppressWarnings("FutureReturnValueIgnored") // for addListener
private void sendHandshake(ChannelHandlerContext ctx) throws GeneralSecurityException {
while (true) {
boolean written = false;
ByteBuf buf = ctx.alloc().buffer(HANDSHAKE_FRAME_SIZE).retain(); // refcnt = 2
try {
handshaker.getBytesToSendToPeer(buf);
if (buf.isReadable()) {
ctx.writeAndFlush(buf).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
written = true;
} else {
break;
}
} catch (GeneralSecurityException e) {
throw new GeneralSecurityException("TsiHandshakeHandler encountered exception", e);
} finally {
buf.release(written ? 1 : 2);
}
}
}
@Override
protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
if (semaphoreAcquired) {
semaphoreRelease();
semaphoreAcquired = false;
}
handshaker.close();
}
private ChannelFuture semaphoreAcquire(ChannelHandlerContext ctx) {
if (semaphore == null) {
return ctx.newSucceededFuture();View on GitHub (pinned to 64daddc1f3)
Solutions
- Inspect the wrapped cause (getCause()) to find the real failure (crypter vs framing vs transport)
- Fail and close the channel, then re-establish the connection with a fresh handshake
- Verify both peers use compatible ALTS/TSI versions and the byte stream isn't corrupted by intermediate proxies
- Enable TsiHandshakeHandler debug logging to identify which frame write failed
Example fix
// pipeline side: handle the failure and close
pipeline().addLast(new TsiHandshakeHandler(peer)) {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace(); // includes the wrapped GeneralSecurityException
ctx.close(); // handshake is unrecoverable; reconnect
}
}; Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
handshakeHandler.sendHandshake(ctx);
} catch (GeneralSecurityException e) {
log.severe("TSI handshake failed: " + e.getCause());
ctx.close(); // close channel and reconnect with a new handshake
} Prevention
- Always inspect getCause() to find the root security failure
- Implement exceptionCaught in the pipeline to close the channel on handshake failure
- Keep ALTS/TSI versions consistent across peers
When it happens
Trigger: A GeneralSecurityException thrown while writing handshake frames during decode(), userEventTriggered(), or a write-future callback (operationComplete) — typically encryption/counter failures or corrupt handshake data while sending.
Common situations: Corrupted handshake bytes on the wire, a failing ALTS crypter (e.g. counter issues), or handshake data written after the peer already aborted the handshake.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Could not get enough key data from the handshake.
- Handshaker service error: ${status.getDetails()}
- Received an unexpected response.
- No handshaker response received
- No ALTS context information found
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/4d7f7a5d3b528f10.
Report an issue: GitHub.