denoland/deno · error · Error
TLS wrap attach failed: ${attachResult}
Error message
TLS wrap attach failed: ${attachResult} What it means
In Deno's tls_wrap internal binding, attachNativeHandle wraps an underlying native TCP or Pipe handle into the TLS layer via res.attach()/res.attachPipe(). A non-zero return value is a native error status, thrown as a plain Error including the status code ('TLS wrap attach failed: <code>'). It signals the native side refused to adopt the handle — typically an invalid, closed, or already-used handle.
Source
Thrown at ext/node/polyfills/internal_binding/tls_wrap.ts:41
: buf.subarray(0, nread);
res.receive(data);
} else if (nread < 0) {
// EOF or error - stop native TCP reads and unref the handle.
// Without this, the libuv handle keeps a ref on the event loop
// and prevents process exit after the TLS connection ends.
nativeHandle.readStop();
nativeHandle.unref();
res.emitEof();
}
};
}
function attachNativeHandle(res: TLSWrap, nativeHandle: any) {
const attachResult = nativeHandle instanceof PipeWrap
? res.attachPipe(nativeHandle)
: res.attach(nativeHandle);
if (attachResult !== 0) {
throw new Error(`TLS wrap attach failed: ${attachResult}`);
}
installNativeOnread(res, nativeHandle);
res._nativeTcpHandle = nativeHandle;
}
/**
* Create a TLSWrap that intercepts an underlying stream handle.
* Mirrors Node's `internalBinding('tls_wrap').wrap(handle, context, isServer)`.
*
* @param handle - The underlying stream handle (TCP CppGC object or JSStreamSocket handle)
* @param context - SecureContext object { ca, cert, key, rejectUnauthorized }
* @param isServer - Whether this is a server-side TLS connection
* @param servername - SNI hostname for client connections
*/
function wrap(
handle: any,
context: any,View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass a fresh, connected, unencrypted net.Socket to tls.connect({ socket })
- If the socket is already a TLSSocket / already encrypted, use it directly instead of wrapping again
- Rebuild the connection: socket.destroy(); sock = net.connect(...); then tls.connect({ socket: sock })
Example fix
// before
const tlsSock = tls.connect({ socket: pooledSocket }); // pooledSocket may be closed/reused
// after
if (pooledSocket.destroyed || pooledSocket.encrypted) {
pooledSocket = net.connect(port, host);
}
const tlsSock = tls.connect({ socket: pooledSocket }); Defensive patterns
Strategy: try-catch
Validate before calling
const usable = socket &&
!socket.destroyed &&
!(socket as any).encrypted &&
typeof socket._handle === 'object';
if (!usable) socket = net.connect(port, host);
const tlsSock = tls.connect({ socket }); Try / catch
try {
tlsSock = tls.connect({ socket });
} catch (e: any) {
if (/TLS wrap attach failed/.test(e?.message ?? '')) {
socket.destroy();
const fresh = net.connect(port, host);
tlsSock = tls.connect({ socket: fresh });
} else throw e;
} Prevention
- tls.connect({ socket }) only over fresh, connected, unencrypted net.Sockets
- Check socket.destroyed and pool age before reusing pooled sockets for TLS
- Never wrap an already-secure TLSSocket a second time
When it happens
Trigger: tls.connect({ socket }) where the net.Socket's native handle is destroyed, already attached to another TLSWrap, or absent (e.g. a socket obtained from an upgrade event or after end()); TLS-over-TLS by re-wapping an already-encrypted TLSSocket; reconnect logic reusing pooled sockets.
Common situations: Proxy/tunnel setups that tls.connect over an existing socket; connection-pool reuse where a socket was closed by the peer; wrapping sockets that Deno materialized from a JS transport instead of a real TCP handle (those take the JS-stream path instead).
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- TLS wrap attach (JS stream) failed: ${attachResult}
- ERR_INVALID_FD_TYPE
- ERR_INVALID_ARG_TYPE
- Unsupported transport: '${transport}'
- ERR_TLS_PROTOCOL_VERSION_CONFLICT
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e40e527e05b1b62e.
Report an issue: GitHub.