grpc/grpc-java · error · IOException

Expected NPN/ALPN ${expectedProtocol}: ${negotiatedProtocol}

Error message

Expected NPN/ALPN ${expectedProtocol}: ${negotiatedProtocol}

What it means

During the TLS server handshake, TlsServerHandshakerSocketFactory negotiates the application protocol via NPN/ALPN and requires it to be exactly h2 (Protocol.HTTP_2). If the client negotiated nothing or a different protocol, it throws IOException 'Expected NPN/ALPN h2: <negotiated>'.

Source

Thrown at okhttp/src/main/java/io/grpc/okhttp/TlsServerHandshakerSocketFactory.java:63

  @Override
  public HandshakeResult handshake(Socket socket, Attributes attributes) throws IOException {
    HandshakeResult result = delegate.handshake(socket, attributes);
    socket = socketFactory.createSocket(result.socket, null, -1, true);
    if (!(socket instanceof SSLSocket)) {
      throw new IOException(
          "SocketFactory " + socketFactory + " did not produce an SSLSocket: " + socket.getClass());
    }
    SSLSocket sslSocket = (SSLSocket) socket;
    sslSocket.setUseClientMode(false);
    connectionSpec.apply(sslSocket, false);
    Protocol expectedProtocol = Protocol.HTTP_2;
    String negotiatedProtocol = OkHttpProtocolNegotiator.get().negotiate(
        sslSocket,
        null,
        connectionSpec.supportsTlsExtensions() ? Arrays.asList(expectedProtocol) : null);
    if (!expectedProtocol.toString().equals(negotiatedProtocol)) {
      throw new IOException("Expected NPN/ALPN " + expectedProtocol + ": " + negotiatedProtocol);
    }
    attributes = result.attributes.toBuilder()
        .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
        .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, sslSocket.getSession())
        .build();
    return new HandshakeResult(socket, attributes,
        new InternalChannelz.Security(new InternalChannelz.Tls(sslSocket.getSession())));
  }
}

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Ensure clients use an HTTP/2-capable stack with ALPN support; upgrade JDK or use a JDK with built-in ALPN
  2. Remove TLS-terminating intermediaries or configure them to pass through ALPN
  3. Set the server's connection spec to include TLS extensions (supportsTlsExtensions) so negotiation happens
  4. Confirm with openssl s_client -alpn h2 that h2 is negotiated end-to-end

Example fix

// before
connSpec = ConnectionSpec.MODERN_TLS; // may omit TLS extensions on some stacks
// after
connSpec = ConnectionSpec.MODERN_TLS.toBuilder().supportsTlsExtensions(true).build();
Defensive patterns

Strategy: try-catch

Validate before calling

// Check ALPN support on the current JVM
boolean alpnOk = java.security.Security.getProperty("jdk.tls.alpnDisabledAlgorithms") != null;

Try / catch

try { result = factory.handshake(socket, attrs); }
catch (IOException e) {
  if (e.getMessage().startsWith("Expected NPN/ALPN")) { /* ensure client supports h2 ALPN or remove TLS terminator */ throw e; }
}

Prevention

When it happens

Trigger: A client completes the TLS handshake without negotiating h2 — e.g. client without ALPN support, ALPN extension stripped by a middlebox/TLS terminator, or connectionSpec without TLS extensions.

Common situations: Old JDKs lacking ALPN (pre-JDK8u251/9+), proxies or load balancers terminating TLS and dropping ALPN, HTTP/1.1-only clients connecting to a gRPC server.

Related errors


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