Troubleshoot “Login Failed for User” (Error 18456) in SQL Server

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

Error 18456 is the most common login failure in SQL Server, and the message you get is deliberately vague:

Msg 18456  ·  Severity 14
Login failed for user 'domain\user'. (Microsoft SQL Server, Error: 18456)
⚡The client normally receives only the meaningless State 1. The real state number, which names the exact cause out of a dozen possibilities, lives in the server error log; decode the state before touching any password.

That is all the client is told, on purpose. If SQL Server explained exactly why a login failed, it would be handing an attacker a checklist. The real reason is written to the SQL Server error log as a state number, and that number is the whole game.

PowerShell running sqlcmd as DemoAppUser with a wrong password. The only output is: Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Login failed for user 'DemoAppUser'. No state number and no reason reach the client.

I have spent more time than I would like reading these states at awkward hours, so here is the version I wish I had found first.


Get the State Number First

Everything below depends on this. Without the state you are guessing. The state numbers are documented on Microsoft Learn; the table below is that list with what each one turns out to be in practice.

-- The two halves of a login failure are on SEPARATE lines in the log,
-- and no single filter returns both: the Error line does not contain 'Login failed',
-- and the Reason line does not contain '18456'. Read twice and union.
DECLARE @log TABLE (LogDate datetime, ProcessInfo nvarchar(64), Txt nvarchar(max));

INSERT INTO @log EXEC xp_readerrorlog 0, 1, N'18456';        -- gives you the State
INSERT INTO @log EXEC xp_readerrorlog 0, 1, N'Login failed'; -- gives you the reason and the client

SELECT TOP (20) LogDate, Txt
FROM   @log
ORDER  BY LogDate DESC;
SSMS results of xp_readerrorlog filtered on 18456 and Login failed. The newest pair reads Error: 18456, Severity: 14, State: 8 and Login failed for user 'DemoAppUser'. Reason: Password did not match that for the login provided. Below it, repeating every 15 minutes, State 38 pairs for NT SERVICE\SQLSERVERAGENT with Reason: Failed to open the explicitly specified database 'WatchtowerMetrics'. Client addresses are blurred.

You are looking for a line like:

Error: 18456, Severity: 14, State: 8.
Login failed for user 'sa'. Reason: Password did not match that for the login provided. [CLIENT: 10.0.0.42]

That State: 8 is the answer. The client normally receives only the generic State 1: the real reason is deliberately kept server-side so failed logins cannot be used to probe accounts, which is why the error log, not the client message, is where diagnosis starts.

If you cannot get into the instance at all, the log is still readable on disk, usually at ...\MSSQL\Log\ERRORLOG, and any text editor will open it.


What Each State Actually Means

StateMeaningWhat it usually is in practice
1Error information is not availableYou do not have permission to see the real state. The server error log has it; ask whoever can read it, or read it yourself if you can.
2User ID not validLogin does not exist. Often a typo, or a login that was dropped
5Login not foundSame as 2 in practice. Check spelling and the domain part
6Windows login used with SQL authenticationThe connection string says SQL auth but the login is a Windows account
7Login disabled and password wrong2 problems at once. Fix the password after enabling
8Password did not matchGenuinely the wrong password. The most common of all
9Password not validDocumented as its own state; in practice treat it exactly like 8
11Valid login, but server access deniedWindows login is valid, has no permission to connect. Often a missing group membership, or access that only comes through the local Administrators group and the client was not started as administrator
12Valid login, server access deniedAs 11, usually a login that exists but was denied CONNECT SQL
18Password must be changedMUST_CHANGE is set on the login
38Database not foundThe login is fine. The database in the connection string does not exist or is offline
46Database requested not foundDocumented alongside 38, same practical meaning: check the database, not the credentials
40Cannot open the default databaseThe login’s default database is offline, dropped, or in single user mode
58SQL login used when only Windows auth is enabledThe instance is in Windows Authentication mode. Microsoft also lists a SID mismatch under this state, which is the rarer cause
62Contained database SID mismatchA Windows account reaching a contained database whose user SID no longer matches, usually after a restore between servers
102-111, 132-133Azure AD / Entra failureCloud authentication problems on Azure SQL, not passwords
122-124Empty user name or passwordThe connection string lost its credentials, common after config templating goes wrong
126Database requested does not existThe Azure-era sibling of 38

States 8, 38 and 40 cover the large majority of real incidents. If you are in a hurry, check those 3 first.


The Fixes, By State

STATE 8

Wrong Password

Confirm the login exists and is not locked out before assuming the password is simply wrong:

SELECT name,
       is_disabled,
       LOGINPROPERTY(name, 'IsLocked')       AS is_locked,
       LOGINPROPERTY(name, 'IsMustChange')   AS must_change,
       LOGINPROPERTY(name, 'BadPasswordCount') AS bad_password_count,
       LOGINPROPERTY(name, 'PasswordLastSetTime') AS password_last_set
FROM   sys.sql_logins
WHERE  name = N'your_login';

A non-zero bad_password_count with a recent PasswordLastSetTime usually means an application is still holding an old password and retrying, which is also how accounts get locked out. Find the application before resetting the password, or you will do this again tomorrow.

In a corporate environment the Windows sibling of this error, 18452, is the one I see more than almost any other, and when I do it is usually the same story as State 8: the account is locked out, or the password changed and the client is still holding the old one.

STATE 38 OR 40

The Database Is the Problem, Not the Login

This is the state that fools people, because the login is perfectly fine. State 38, and 46 which Microsoft lists alongside it, means the database the connection string asked for: the reason line reads Failed to open the explicitly specified database 'YourDb'. State 40 means the login’s default database instead, and its reason line reads Failed to open the database 'YourDb' specified in the login properties. Check the database either way:

-- Is the database actually there and online?
SELECT name, state_desc, user_access_desc, is_read_only
FROM   sys.databases
WHERE  name = N'your_database';

If state_desc is anything other than ONLINE, that is your answer. RECOVERING, SUSPECT and OFFLINE all produce a login failure that looks like a permissions problem. If the database is not there at all and this is State 38, the fix is on the client: the connection string’s Initial Catalog names a database that was renamed, dropped or never existed on this instance.

For State 40 the login’s default database is the problem, and the login can be fixed without touching the database:

ALTER LOGIN [your_login] WITH DEFAULT_DATABASE = [master];
STATE 11 OR 12

Valid Login, No Access

Almost always a Windows login whose access came through a group that has changed. Check what the server actually thinks the login can do:

SELECT sp.name,
       sp.type_desc,
       sp.is_disabled,
       spm.permission_name,
       spm.state_desc
FROM   sys.server_principals sp
LEFT JOIN sys.server_permissions spm
       ON spm.grantee_principal_id = sp.principal_id
      AND spm.permission_name = 'CONNECT SQL'
WHERE  sp.name = N'DOMAIN\your_login';

No CONNECT SQL grant, or a DENY, is the cause:

GRANT CONNECT SQL TO [DOMAIN\your_login];

If the grant is there and State 11 keeps coming, read the reason line. Token-based server access validation failed with an infrastructure error usually means the user’s only route in is the local Administrators group, and Windows does not present that membership unless the client is elevated. Start SSMS with Run as administrator to get in, then add the Windows user as a login in its own right so it stops depending on the group.

STATE 58

SQL Login on a Windows-Auth-Only Instance

-- 1 = Windows Authentication only, 0 = Mixed Mode
SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS windows_auth_only;

If this returns 1 and you need SQL logins, the instance must be switched to Mixed Mode, which requires a service restart. That is a change-window job, not a quick fix.

STATE 6

Windows Login, SQL Auth Connection String

Nothing is wrong with the server. The connection string is using a username and password for an account that is a Windows login. Switch it to integrated security.


The One That Is Not a Login Problem at All

If the error log shows no 18456 entry at the time of the failure, the connection never reached the authentication stage. That is a network, TLS or protocol problem wearing a login error’s clothing, and you should be looking at the pre-login handshake instead.


Stopping It Recurring

  • Lockouts that keep coming back are almost always a service, scheduled task or connection pool holding a stale password. Find it before resetting.
  • State 38 in bulk usually means an application is connecting to a database that was renamed or dropped, and nobody updated the connection string.
  • Grant server access through groups, not individual logins. State 11 and 12 mostly happen when a person’s group membership changes and their individual grant was never there to begin with.

Frequently Asked Questions

Why does the client only ever show State 1?
Deliberately. Telling the client the real reason would hand an attacker a checklist of which part of the login was wrong. The true state is written to the SQL Server error log instead, which is why reading the log is the first step rather than the last.
The password is definitely right, so why State 8?
Check whether the login is locked out rather than wrong. A non-zero BadPasswordCount with a recent password change usually means an application is still retrying an old password, which is also what locks the account.
There is no 18456 in the error log at all. What now?
Then the connection never reached authentication, and this is not a login problem. Look at the pre-login handshake instead, which fails earlier and reports a different error.
Lots of 18456s and I do not know which machine they are coming from.
The error log records the caller on every one. Read it with EXEC xp_readerrorlog 0, 1, N'Login failed'; and each entry ends with the source, like [CLIENT: 10.0.0.25], alongside the login name and the reason. That turns “something is hammering us” into an address you can chase. Watch for a regular interval in the timestamps: a failure landing every 15 minutes on the dot is a scheduled job or a monitoring agent, not a person mistyping a password, and the reason text usually names the database it could not open.
How do I find the state on Azure SQL Database?
Not from an error log: Azure SQL Database has none, so xp_readerrorlog is not there to call. Failed logins, with their reason, are recorded by auditing on the server or database. Managed Instance has an error log and the query above works as it is.

Where To Go Next

Which of these you need depends on the state number in the error log.

Comments

Leave a Reply

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