Skip to content

grpc-js: secure channel to an IP address fails on Node 25+ (servername set to IP literal) #3127

Open

Description

@w1am

Problem description

Credentials from createSsl() cannot connect to a target given by IP address on Node 25 and later.

getConnectionOptions in channel-credentials.ts sets servername = remoteHost unconditionally (since #1466). Node 25 made DEP0123 End-of-Life (nodejs/node#58533), so tls.connect now throws instead of warning when servername is an IP literal:

ERR_INVALID_ARG_VALUE: The property 'options.servername' Setting the TLS ServerName to an IP address is not permitted.. Received '127.0.0.1'

The throw happens inside the connect promise in SecureConnectorImpl.connect, so the caller only ever sees 14 UNAVAILABLE: No connection established. Hostname targets work. Node 26 is the current LTS, so this now hits default installs.

Reproduction steps

Any TLS gRPC server listening on 127.0.0.1:PORT, then:

const grpc = require('@grpc/grpc-js');
const client = new grpc.Client('127.0.0.1:PORT',
  grpc.credentials.createSsl(null, null, null, { rejectUnauthorized: false }));
client.waitForReady(Date.now() + 2000, err => console.log(err ? err.message : 'connected'));

Node 24: connected
Node 26: Failed to connect before the deadline

Wrapping tls.connect to log its options shows servername: '127.0.0.1' on every attempt, and the synchronous ERR_INVALID_ARG_VALUE throw.

Environment

  • Linux 7.0, amd64
  • Node v26.13.0 (works on v24.17.0)
  • fnm
  • @grpc/grpc-js 1.14.7, same code on master

Additional context

Node's own https agent already skips SNI for IP literals. Same one-line change here:

if (!net.isIP(remoteHost)) {
  connectionOptions.servername = remoteHost;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions