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
- Make the custom factory's createSocket overloads return SSLSockets built from an SSLContext.getSocketFactory() wrapping the delegate socket
- Prefer TlsServerCredentials.keyManager/trustManager so gRPC constructs TLS sockets itself
- 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
- Test custom SocketFactory createSocket overloads before wiring into TlsServerCredentials
- Use server-side TLS-aware factories only
- Default to TlsServerCredentials.keyManager/trustManager instead of custom factories
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
- SocketFactory ${socketFactory} did not produce an SSLSocket:
- Expected NPN/ALPN ${expectedProtocol}: ${negotiatedProtocol}
- Can't set TLS settings for ALTS
- Unexpected error converting ChannelCredentials to Netty SslC
- Failed to build SSL context from certificate files: ${e}
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/55d57ca79c212a63.
Report an issue: GitHub.