A Connection Was Successfully Established, But Then an Error Occurred During Login

🚨Part of the SQL Server Errors series, the exact messages and what actually causes them.In: Security

The connection reached the server’s port and then died before the login was ever read, so the password is not the problem and there will be no 18456 in the error log.

TCP Provider  ·  error: 0
A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
⚡Read the bracket at the end first. SSL Provider means the TLS negotiation failed (TLS versions and ciphers). TCP Provider with a closed connection means the far end hung up before SQL Server answered (what closed it). A pre-login timeout means something took the connection and never answered (nothing answered). Credentials are never checked this early.

That exact text is one of the two forms MS Docs lists under An existing connection was forcibly closed (OS error 10054). The other reads during the login process with SSL Provider. Both are this post.


Which Form You Have

The order is fixed. The TCP connection opens, and that part worked. The client sends a PRELOGIN message, the server answers with its version and encryption setting, a TLS handshake follows if encryption was agreed, and only then is the login sent (MS-TDS PRELOGIN on MS Docs). SQL Server encrypts the user name and password even when the rest of the connection is not encrypted (MS Docs), so a broken TLS setup breaks every connection, not just the encrypted ones.

So the text after error: 0 tells you which step failed:

sqlcmd and anything else on ODBC Driver 18 word the same failure differently, with the cause on the second line:

sqlcmd (ODBC Driver 18) against a port that drops the connection at pre-loginthe symptom, not something to copy
> sqlcmd -S HPAI01,14436 -E -C -l 5 -Q "SELECT 1" Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection because an error was encountered during handshakes before login. Common causes include client attempting to connect to an unsupported version of SQL Server, server too busy to accept new connections or a resource limitation (memory or maximum allowed connections) on the server.. Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : TCP Provider: An existing connection was forcibly closed by the remote host. . Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection. Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Client unable to establish connection due to prelogin failure.

TLS Versions and Cipher Suites

The most common cause today. TLS 1.0 and 1.1 are disabled by default on Windows 11, Windows Server 2022 and later, and Group Policy often switches them off on older builds. When the client and the server share no TLS version, or no cipher suite, the server closes the connection instead of answering the Client Hello (MS Docs). From a .NET application it reads:

SqlConnection.Open() in System.Data.SqlClient, TLS handshake dropped by the serverthe symptom, not something to copy
A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - An existing connection was forcibly closed by the remote host.)

Check the build first:

SELECT SERVERPROPERTY('ProductVersion')   AS version,
       SERVERPROPERTY('ProductLevel')     AS service_pack,
       SERVERPROPERTY('ProductUpdateLevel') AS cu,
       SERVERPROPERTY('Edition')          AS edition;

SQL Server 2016 and later support TLS 1.2 natively. Older builds need an update: 2014 SP1 CU5 (12.0.4439.1) or RTM CU12, 2012 SP2 CU10 (11.0.5644.2) or SP3 CU1, and the TLS 1.2 updates for 2008 R2 SP3 (10.50.6542.0) and 2008 SP4 (10.0.6547.0). Every row, including the GDR branches, is in TLS 1.2 support for SQL Server on MS Docs.

The client is the half people forget. SQL Server Native Client 10 and 11 need their updates, ODBC Driver 13, 17 and 18 support TLS 1.2 natively, and .NET Framework applications want 4.6.2 or later. An up-to-date server still fails against an application on an old client library.

The enabled protocols live in the registry under:

HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols

Check both machines. The mismatch is by definition between them. Get-TlsCipherSuite lists the enabled cipher suites on each side, and a client and server on different Windows versions that both offer TLS_DHE_* suites are a documented cause on their own.


The Certificate

A server certificate with a weak thumbprint algorithm (MD5, SHA224 or SHA512) does not work with TLS 1.2 and produces this error. Self-signed certificates are not affected (MS Docs). Find the certificate the instance loaded:

EXEC xp_readerrorlog 0, 1, N'certificate', NULL, NULL, NULL, N'DESC';

A certificate the client simply does not trust fails with a different message, The certificate chain was issued by an authority that is not trusted, and that has its own fix. A bound certificate that fails the requirements, such as a private key the service account cannot read, does not produce this error either: SQL Server will not start at all (MS Docs).


Something Closed the Connection

“An existing connection was forcibly closed by the remote host” is Windows socket error 10054: the far end, or a device in between, ended the connection before SQL Server answered the pre-login. A hard reset can read The specified network name is no longer available in the same message instead. If the drop comes one step later, after the pre-login was answered, the same client says during the login process, still with TCP Provider.

Something has to send it:

  • A firewall, IPS or proxy inspecting the SQL Server port and dropping traffic it does not understand
  • A VPN or NAT device in the path
  • A different TLS policy on one side, see TLS Versions and Cipher Suites, which ends the connection the same way

The tell is that it works from some clients and not others, or works locally on the server and fails across the network. When it is intermittent, take a network trace on the client and the server at the same time and compare the Client Hello and Server Hello (MS Docs).


Nothing Answered the Pre-Login

The TCP connection opened, but nothing sent the pre-login answer back. A web server sitting on the port the client was pointed at produces exactly this:

SqlConnection.Open() in System.Data.SqlClient against a web server on the SQL Server portthe symptom, not something to copy
Connection Timeout Expired. The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. This could be because the pre-login handshake failed or the server was unable to respond back in time. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=37; handshake=4676;

sqlcmd says the same thing as Unable to complete login process due to delay in prelogin response. Check the port first: an instance that moved to a dynamic port, or a connection string pointed at the wrong one, reaches whatever else is listening there (see SQL Server Default Ports). On .NET Framework, a DNS name that resolves to several addresses (an availability group listener, a multi-subnet cluster) tries them one at a time, and a dead address first surfaces as this timeout: set MultiSubnetFailover=True (MS Docs).


The Server Is Too Busy to Answer

Occasionally the instance itself is the cause. The ODBC message names it outright, server too busy to accept new connections or a resource limitation (memory or maximum allowed connections), and MS Docs documents intermittent 10054 errors on busy SQL Server 2017 and earlier when connection-accepting workers run short (MS Docs). Check whether the error log shows the instance under stress at the same timestamps:

EXEC xp_readerrorlog 0, 1, N'login', NULL, NULL, NULL, N'DESC';

Nothing at all around the failure time supports the network or TLS explanation, since the instance never saw a login attempt.


Working Out Which One You Have

-- What are current connections actually negotiating?
SELECT  c.session_id,
        c.encrypt_option,
        c.protocol_type,
        c.net_transport,
        c.client_net_address,
        s.program_name
FROM    sys.dm_exec_connections c
JOIN    sys.dm_exec_sessions   s ON s.session_id = c.session_id
WHERE   c.session_id > 50;

Then narrow it down by elimination:

  • Connect locally on the server, if that works, the instance is fine and the problem is in the path or the client
  • Try a different client machine, works elsewhere means a client-side TLS or driver issue
  • Try sqlcmd -S server -C, trusting the certificate isolates certificate trust from protocol mismatch
  • Do not expect Encrypt=False to get round it, the login packet is encrypted anyway, so a TLS version or cipher mismatch fails the same way with encryption off

Stopping It Recurring

  • Patch SQL Server and client drivers together. The commonest version of this incident is a Windows update disabling old TLS on a server whose SQL build never supported the new one.
  • Standardise on ODBC Driver 18 or later and set encryption behaviour deliberately rather than relying on defaults, which have changed and will change again (ODBC Driver 18 made Encrypt default to Yes, MS Docs).
  • Keep TLS and cipher suite policy the same on both ends. A hardening change applied to the servers and not the application hosts, or the other way round, is this error on the next connection.
  • Diary certificate expiry. SQL Server keeps running on a certificate that expires after it was configured, and clients that check validity on every connection start failing (MS Docs).

Frequently Asked Questions

SSMS connects fine from the same machine but the application fails.
Different client stacks. SSMS carries its own driver and the application uses whatever it was built against, and Microsoft.Data.SqlClient changed its default to Encrypt=true at version 4.0, listed by MS Docs among the version-specific changes. An application that picked up a newer driver starts demanding encryption against a server whose certificate it will not accept, while SSMS on the same box carries on. Check the driver version and the connection string’s Encrypt setting before you touch the server.
Should I check the firewall or the port?
Not for reachability: the message says the connection was established, so the port answered. A blocked port gives a connect failure with a different message entirely. But two port-side causes still fit. Something other than SQL Server can be listening on the port the client was given, which shows up as a pre-login timeout, and a firewall or IPS on the path can reset a connection it has already let through. What is left after that is protocol, encryption or certificate.
Is TrustServerCertificate=True the same as turning encryption off?
No, and the difference matters. TrustServerCertificate=True keeps the channel encrypted and skips validating the certificate, so it connects you while an expired or untrusted certificate is still sitting there. Encrypt=False asks for no encryption at all and will not help you if the server has Force Encryption on, because the server still requires it. Use the first to confirm the certificate is the cause, then go and fix the certificate.
The connections query returns rows and they all look fine.
sys.dm_exec_connections can only show connections that got through, so it tells you nothing about the client that cannot. Use it to establish what a working client negotiates, then compare that against the failing client’s driver and connection string. The failed attempt leaves no row anywhere, which is the same reason there is no 18456 in the error log.
Is “Client unable to establish connection because an error was encountered during handshakes before login” the same error?
Yes. That is how sqlcmd and other ODBC Driver 18 clients word it, followed by the provider line (TCP Provider or SSL Provider) that SqlClient puts in brackets, and then Client unable to establish connection due to prelogin failure. Read that provider line exactly as you would read the bracket.

Where To Go Next

This error sits between the network and the login. The neighbours on either side:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *