grpc/grpc-java · error · IOException

SocketFactory ${socketFactory} did not produce an SSLSocket:

Error message

SocketFactory ${socketFactory} did not produce an SSLSocket: ${socket.getClass()}

What it means

TlsServerHandshakerSocketFactory wraps a user-supplied SocketFactory: after the delegate handshake, it layers TLS by calling socketFactory.createSocket(...). The result must be an SSLSocket; otherwise it throws IOException 'SocketFactory <f> did not produce an SSLSocket: <class>'.

Source

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

 * TLS handshaker.
 */
final class TlsServerHandshakerSocketFactory implements HandshakerSocketFactory {
  private final PlaintextHandshakerSocketFactory delegate = new PlaintextHandshakerSocketFactory();
  private final SSLSocketFactory socketFactory;
  private final ConnectionSpec connectionSpec;

  public TlsServerHandshakerSocketFactory(
      SslSocketFactoryServerCredentials.ServerCredentials credentials) {
    this.socketFactory = credentials.getFactory();
    this.connectionSpec = credentials.getConnectionSpec();
  }

  @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,

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Make the custom factory's createSocket overloads return SSLSockets built from an SSLContext.getSocketFactory() wrapping the delegate socket
  2. Prefer TlsServerCredentials.keyManager/trustManager so gRPC constructs TLS sockets itself
  3. Confirm the factory passed to TlsServerCredentials.Builder is a factory intended for server-side TLS layering

Example fix

// before
return new Socket(server, port);
// after
return tlsContext.getSocketFactory().createSocket(socket, host, port, autoClose);
Defensive patterns

Strategy: validation

Validate before calling

Socket s = myFactory.createSocket();
if (!(s instanceof SSLSocket)) throw new IllegalStateException("Custom socket factory must produce SSLSocket");

Try / catch

try { transport = handshake(socket, attrs); }
catch (IOException e) {
  if (e.getMessage().contains("did not produce an SSLSocket")) { /* replace or fix factory */ }
}

Prevention

When it happens

Trigger: Using TlsServerCredentials with a custom SocketFactory (customSSLSocketFactory) whose createSocket(Socket, host, port, autoClose) returns a plain Socket during server handshake.

Common situations: Custom socket factories that ignore the autoClose/delegate contract, factories implemented for client-side use only, test stubs returning raw sockets.

Related errors


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