Login failed for user 'sa'. (error 18456) means SQL Server received the login and refused it. The client is never told why: it always sees state 1. The real reason is a state number in the server's error log (8 for a wrong password, 5 for a login that does not exist), so read that line first and fix the one thing it names.

In a SQL Server container, a cause that is easy to miss is a reused data volume. A changed MSSQL_SA_PASSWORD did not change the password stored on an existing volume in this test, so connect with the old password and run ALTER LOGIN sa WITH PASSWORD, or start again on a fresh volume if its data is disposable.

The error

PS> .\FixLab.exe 'Server=127.0.0.1,15433;User ID=sa;Password=Second-Pass-2026x;TrustServerCertificate=true' 'SELECT SUSER_SNAME()'
ex.Number = 18456, ex.Class = 14, ex.State = 1
Microsoft.Data.SqlClient.SqlException: Login failed for user 'sa'.

sqlcmd reports the same refusal as Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : Login failed for user 'sa'..

Why it happens

The container was created with MSSQL_SA_PASSWORD=First-Pass-2026x on the named volume fix7-mssql-data (container, volume and passwords are the test's own), removed, and created again on the same volume with Second-Pass-2026x. The master database on the volume still held the first password: the second one failed, the first one still worked.

The message is vague on purpose. Microsoft's MSSQLSERVER_18456 page says the text sent to the client "deliberately hides the nature of the authentication error". The server log keeps it. These lines came from docker logs fix7-mssql after a wrong password, a login that does not exist and a disabled login:

2026-10-30 02:50:00.88 Logon       Error: 18456, Severity: 14, State: 8.
2026-10-30 02:50:00.88 Logon       Login failed for user 'sa'. Reason: Password did not match that for the login provided. [CLIENT: 172.17.0.1]
2026-10-30 02:50:01.25 Logon       Error: 18456, Severity: 14, State: 5.
2026-10-30 02:50:01.25 Logon       Login failed for user 'report_user'. Reason: Could not find a login matching the name provided. [CLIENT: 172.17.0.1]
2026-10-30 02:50:01.75 Logon       Error: 18470, Severity: 14, State: 1.
2026-10-30 02:50:01.75 Logon       Login failed for user 'report_reader'. Reason: The account is disabled. [CLIENT: 172.17.0.1]

The same page lists the other common states: 7 is a disabled login with a wrong password, 38 a database that could not be found, 58 a SQL login sent to a server that accepts Windows logins only. A disabled login with the right password came back as error 18470, and that one does name its reason to the client. On a server without docker logs, run EXEC sp_readerrorlog 0, 1, N'Login failed' as an administrator.

LocalDB behaves differently. A SQL login that does not exist returned 18456 with state 5 in its log, but sa returned error 233, while its log recorded 18456 state 7, "An error occurred while evaluating the password."

The fix

PS> .\FixLab.exe 'Server=127.0.0.1,15433;User ID=sa;Password=First-Pass-2026x;TrustServerCertificate=true' 'ALTER LOGIN sa WITH PASSWORD = ''Second-Pass-2026x'''
OK (-1 row(s) affected) in 0.2 s

PS> .\FixLab.exe 'Server=127.0.0.1,15433;User ID=sa;Password=Second-Pass-2026x;TrustServerCertificate=true' 'SELECT SUSER_SNAME()'
sa
OK (-1 row(s) affected) in 0.2 s

After ALTER LOGIN the password in the compose file and the one on the volume agree. Keep TrustServerCertificate=true for a local container: without it SqlClient stopped before the login with the certificate chain error. Where the server runs on Windows, as LocalDB does, Windows authentication avoids passwords altogether (the 0 means this LocalDB accepts SQL logins as well):

PS> .\FixLab.exe 'Server=(localdb)\MSSQLLocalDB;Integrated Security=true' 'SELECT SUSER_SNAME(), SERVERPROPERTY(''IsIntegratedSecurityOnly'')'
MACHINE\user | 0
OK (-1 row(s) affected) in 0.4 s

How it was reproduced

A console app from dotnet new console -n FixLab -f net10.0 (.NET SDK 10.0.403) with Microsoft.Data.SqlClient 7.1.1 runs the SQL in its second argument and prints Number, Class and State of any SqlException. The server was mcr.microsoft.com/mssql/server:2022-latest (16.0.4305.5, CU27) published on 127.0.0.1:15433, plus SQL Server 2022 LocalDB 16.0.1210.3. sqlcmd 16.0.1000.6 on Windows and the image's own sqlcmd (ODBC Driver 18) gave the same message.

Frequently asked

What does state 8 mean in SQL Server error 18456?
The login exists but the password is wrong. The state appears only in the server error log, as Error: 18456, Severity: 14, State: 8. The client always sees state 1.
Why does my SQL Server Docker container reject the new SA password?
In this test a new MSSQL_SA_PASSWORD did not change the password on an existing data volume. Connect with the old password and run ALTER LOGIN sa WITH PASSWORD, or remove the volume if its data is disposable.
How do I see why a SQL Server login failed?
Read the server error log: docker logs for a container, or EXEC sp_readerrorlog 0, 1, N'Login failed' as an administrator. The Reason text names the cause, such as Password did not match or Could not find a login matching the name provided.

More decoded errors in the Fixes category.