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

  1. Inspect the wrapped cause (getCause()) to find the real failure (crypter vs framing vs transport)
  2. Fail and close the channel, then re-establish the connection with a fresh handshake
  3. Verify both peers use compatible ALTS/TSI versions and the byte stream isn't corrupted by intermediate proxies
  4. 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

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

Related errors


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