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
- Only call setServername on client sockets created via tls.connect(), never on sockets passed to tls.createServer's connection callback
- Branch on the socket role before configuring: if (!socket._tlsOptions?.isServer) socket.setServername(name)
- 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
- Use SNICallback/addContext on servers; setServername only on tls.connect sockets
- Keep client and server socket configuration code in separate functions
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- ERR_TLS_REQUIRED_SERVER_NAME
- A key and certificate are required for `Deno.listenTls`
- ERR_INVALID_ARG_TYPE
- fetch failed
- Unsupported 'certFile' / 'keyFile' options provided: use 'ce
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/0753f7f34996ccec.
Report an issue: GitHub.