denoland/deno · error · Error

ERR_TLS_SNI_FROM_SERVER

ERR_TLS_SNI_FROM_SERVER

Error message

Cannot issue SNI from a TLS server-side socket

What it means

TLS Server Name Indication is sent only by clients; a server never names itself. TLSSocket.prototype.setServername throws ERR_TLS_SNI_FROM_SERVER (ext/node/polyfills/_tls_wrap.js:1065) when called on a socket created with _tlsOptions.isServer — i.e. a socket accepted by a tls.Server. The check mirrors Node.js exactly.

Source

Thrown at ext/node/polyfills/_tls_wrap.js:1065

    tcpHandle.readStart();
  }

  // Kick-start the TLS readable side. During start(), the handshake cycle
  // may have received and processed a close_notify (peer called end() before
  // we set up event listeners). The decrypted EOF is buffered in pending_eof
  // because inner.onread wasn't set yet. Call readStart() directly on the
  // TLSWrap to install onread and flush any pending data/EOF.
  if (this._handle) {
    this._handle.readStart();
  }
};

TLSSocket.prototype.setServername = function (name) {
  if (typeof name !== "string") {
    throw new ERR_INVALID_ARG_TYPE("name", "string", name);
  }
  if (this._tlsOptions?.isServer) {
    throw new ERR_TLS_SNI_FROM_SERVER();
  }
  this._handle?.setServername(name);
};

// Format prefixes for the synthetic session buffers we emit from
// onConnectSecure when rustls handles resumption internally. The encoded
// payload is `${servername ?? host ?? ""}:${port ?? ""}` -- see the
// matching emit sites in onConnectSecure.
const SYNTHETIC_SESSION_PREFIXES = [
  "deno-tls12-session:",
  "deno-tls13-session-ticket-1:",
  "deno-tls13-session-ticket-2:",
  "deno-tls13-dummy-session:",
];

function syntheticSessionMatches(buf, options) {
  if (!buf || !options) return false;
  const sessionKey = `${options.servername ?? options.host ?? ""}:${

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Only call setServername on client sockets created via tls.connect(), never on sockets passed to tls.createServer's connection callback
  2. Branch on the socket role before configuring: if (!socket._tlsOptions?.isServer) socket.setServername(name)
  3. On the server side, select certificates per-SNI via server.addContext(servername, ctx) or the SNICallback option instead

Example fix

// before
const server = tls.createServer(opts, (socket) => {
  socket.setServername('example.com'); // ERR_TLS_SNI_FROM_SERVER
});

// after
const server = tls.createServer({
  ...opts,
  SNICallback: (servername, cb) => cb(null, ctxFor(servername)),
});
Defensive patterns

Strategy: validation

Validate before calling

if (socket._tlsOptions?.isServer) throw new Error('cannot set SNI on a server-side socket');

Type guard

function isClientTls(socket) { return socket?._tlsOptions?.isServer !== true; }

Try / catch

try { socket.setServername(name); } catch (e) { if (e.code === 'ERR_TLS_SNI_FROM_SERVER') { /* configure SNI via SNICallback instead */ } else throw e; }

Prevention

When it happens

Trigger: Inside a tls.createServer connection handler, calling socket.setServername(...) on the accepted TLSSocket; sharing a setServername call path between client and server sockets without checking socket._tlsOptions.isServer.

Common situations: Generic TLS wrapper libraries that apply the same configuration function to both outbound (connect) and inbound (accept) sockets; code copied from a client example reused in a server; attempting to mutate SNI after the handshake on the server side.

Understand the failure class

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/0753f7f34996ccec. Report an issue: GitHub.