Active Directory · Troubleshooting

Event 4625 — An Account Failed to Log On

Event 4625 fires on every failed logon anywhere in the domain, and the event’s own Failure Reason text is generic — the numeric Logon Type, Status, and Sub Status fields are what actually separate a mistyped password from a locked-out account or a credential-stuffing attempt.

Quick idea: Event 4625 means a logon attempt failed somewhere in the environment — a domain controller, a member server, or a workstation. The event’s Failure Reason text is generic, so the hexadecimal Status and Sub Status codes, together with the numeric Logon Type, are what tell you whether you are looking at a stale saved password, a locked-out account, or someone actively guessing credentials.
Logon Type

A numeric code describing how the logon was attempted — console, network, RDP, service, and more.

Status / Sub Status

Hexadecimal NTSTATUS codes that carry the actual failure reason, decoded against the MS-ERREF reference.

Failure Reason

A short, generic human-readable string tied to the Status field — never specific enough on its own.

Introduction

A user’s account keeps locking out and nobody can say why. Someone opens the Security log on the domain controller that issued the lockout and finds a wall of Event ID 4625 entries — same account, same rough time window, and a Failure Reason that just reads “Unknown user name or bad password” on every single one. That text alone does not say whether the account is being hit by a phone with a stale saved Wi-Fi password, a scheduled task still holding last month’s credential, or someone at a keyboard three time zones away actually guessing.

The two fields that answer it — Logon Type and the hexadecimal Status / Sub Status pair — sit further down the event than most people scroll before deciding it is not worth reading closely.

What You’re Seeing

A typical 4625 on a member server, from a failed Remote Desktop logon with a wrong password, renders with these fields:

# Security log on the target server, Event ID 4625, Task Category: Logon
An account failed to log on.

Subject:
    Security ID:        NT AUTHORITY\SYSTEM
    Account Name:        FIN-APP01$
    Account Domain:        CORP

Logon Type:            10

Account For Which Logon Failed:
    Security ID:        S-1-0-0
    Account Name:        jdoe
    Account Domain:        CORP

Failure Information:
    Failure Reason:        Unknown user name or bad password.
    Status:            0xC000006D
    Sub Status:            0xC000006A

Network Information:
    Workstation Name:        FIN-LAPTOP03
    Source Network Address:    10.20.4.118
    Source Port:            51422

Detailed Authentication Information:
    Logon Process:        NtLmSsp
    Authentication Package:    NTLM
    Package Name (NTLM only):    NTLM V2

A locked-out account looks similar but carries a different Status pair and, notably, no Sub Status detail:

# Same server, same event ID, account already locked out
Logon Type:            3

Failure Information:
    Failure Reason:        Account locked out.
    Status:            0xC0000234
    Sub Status:            0x0
Important: Event 4625 is generated by the two audit subcategories Logon and Account Lockout, both under the Logon/Logoff audit category. Both are enabled for failure auditing in most modern security baselines, but if 4625 is silent where you expect it, confirm with auditpol /get /category:"Logon/Logoff" before assuming nothing is failing.

What It Actually Means

Think of Status and Sub Status as an outer envelope and the letter inside it. For the credential-family failures — wrong password, account does not exist — Windows almost always writes the same generic outer code, 0xC000006D (STATUS_LOGON_FAILURE equivalent, described as “the attempted logon is invalid, either due to a bad username or authentication information”), into Status. The Sub Status field is where the specific reason actually lives: 0xC0000064 means the account does not exist at all, while 0xC000006A means the username is valid but the password supplied is wrong.

Other failure categories skip that indirection entirely and write the specific code straight into Status, leaving Sub Status at 0x0 — a locked-out account reports Status = 0xC0000234 directly, a disabled account reports 0xC0000072, and an expired account reports 0xC0000193. Knowing which pattern applies is the difference between reading the right field and staring at a generic code that tells you nothing.

Logon Type answers a different question entirely: not why the logon failed, but how it was being attempted. A failed Type 2 means someone typed a wrong password at a physical keyboard; a failed Type 10 means the same wrong password over Remote Desktop; a failed Type 3 against a service account from an unfamiliar IP address is a very different story from a failed Type 2 on the account owner’s usual workstation.

Reading the Logon Type Codes

The Logon Type field uses a fixed set of numeric codes, documented alongside Event 4624 (the successful counterpart to 4625) and shared by both events:

Type Name Meaning
2InteractiveA user logged on at the local keyboard and console.
3NetworkA user or computer logged on from the network — SMB share access, most service-to-service calls.
4BatchUsed by scheduled tasks and batch servers running on behalf of a user without direct interaction.
5ServiceThe Service Control Manager started a service under this account.
7UnlockA locked workstation was unlocked.
8NetworkCleartextA network logon where the password crossed in unhashed form — typically Basic authentication over IIS.
9NewCredentialsA process cloned its token and supplied different credentials for outbound network connections, e.g. runas /netonly.
10RemoteInteractiveTerminal Services or Remote Desktop.
11CachedInteractiveLogon using credentials cached locally; the domain controller was not contacted.
Key point: A failed Type 4 or Type 5 logon from a member of a domain administrative group is worth escalating on its own, independent of the failure reason — Microsoft’s own monitoring guidance for this event flags a mismatch between logon type and account privilege as a signal worth watching, not just the failure itself.

How to Diagnose

Work from the log outward, and pull the account, network, and status detail together before deciding what kind of problem this is.

# 1. Pull recent 4625 events and see what's actually in the log
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4625 } -MaxEvents 200 |
    Select-Object TimeCreated, Id, Message

# 2. Break events down by Status/Sub Status and Logon Type to separate noise from real problems
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4625 } -MaxEvents 500 |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $data = $xml.Event.EventData.Data
        [pscustomobject]@{
            Time      = $_.TimeCreated
            Account   = ($data | Where-Object Name -eq 'TargetUserName').'#text'
            Source    = ($data | Where-Object Name -eq 'IpAddress').'#text'
            LogonType = ($data | Where-Object Name -eq 'LogonType').'#text'
            Status    = ($data | Where-Object Name -eq 'Status').'#text'
            SubStatus = ($data | Where-Object Name -eq 'SubStatus').'#text'
        }
    } | Group-Object Status, SubStatus | Sort-Object Count -Descending

# 3. Check whether the target account is disabled, locked out, or has an expired password
Get-ADUser -Identity jdoe -Properties Enabled, LockedOut, PasswordExpired, PasswordLastSet, BadLogonCount |
    Select-Object Name, Enabled, LockedOut, PasswordExpired, PasswordLastSet, BadLogonCount

# 4. List every currently locked-out account in one pass
Search-ADAccount -LockedOut | Select-Object Name, SamAccountName, LockedOut

# 5. Confirm the audit subcategories that generate 4625 are actually enabled
auditpol /get /category:"Logon/Logoff"
Practical note: A burst of Type 3 failures against a service account from a Source Network Address it has never authenticated from before deserves attention regardless of Status code. A single Type 2 failure followed by a successful Event 4624 seconds later is almost always just a mistyped password.

Common Causes

Cause How to Confirm Fix
Wrong password (Status 0xC000006D, Sub Status 0xC000006A) Often a mapped drive, scheduled task, phone, or browser still holding an old password. Find and update the stale credential source rather than resetting the account’s password again: check Get-ADUser -Identity <name> -Properties BadLogonCount, LastBadPasswordAttempt.
Account does not exist (Status 0xC000006D, Sub Status 0xC0000064) Get-ADUser -Identity <name> returns nothing for the account named in the event. Usually a typo or a decommissioned account still referenced somewhere; if the source address is unexpected, treat repeated attempts as reconnaissance.
Account locked out (Status 0xC0000234) Search-ADAccount -LockedOut lists the account generating the events. Confirm the lockout was triggered by a real bad-password pattern before unlocking with Unlock-ADAccount -Identity <name> — see the account lockouts post for the full policy picture.
Account disabled (Status 0xC0000072) Get-ADUser -Identity <name> -Properties Enabled returns False. Confirm the disable was intentional; re-enable only if it was accidental, otherwise treat repeated attempts as noise from a device that has not been decommissioned.
Account or password expired (Status 0xC0000193 or 0xC0000071) Get-ADUser -Identity <name> -Properties AccountExpirationDate, PasswordExpired shows the expiry. Extend the account expiration date if the account should still be active, or have the user change an expired password through a normal logon.
No domain controller available to validate the logon (Status 0xC000005E) Failures cluster around a single site or subnet and coincide with DC or network outages. This is a DC availability problem, not a credential problem — see the no logon servers available post for the diagnostic path.
Genuine password-spray or brute-force attempt Many distinct accounts, one Status/Sub Status pair, one source address, tight timing — or one high-value account hit from an address it has never used before. Block the source at the firewall or conditional access layer, force a reset on any account that shows a corresponding successful 4624 afterwards, and confirm the lockout threshold actually fired where it should have.

Working Through a Fix

Group the flood by Status and Sub Status before touching a single account — the script in step 2 of the diagnosis section does this in one pass. In most environments the bulk of volume turns out to be one or two accounts repeatedly hitting 0xC000006A from a device that has not been re-pointed at a current password.

# Find which accounts are generating the most wrong-password events right now
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4625 } -MaxEvents 1000 |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $data = $xml.Event.EventData.Data
        $subStatus = ($data | Where-Object Name -eq 'SubStatus').'#text'
        if ($subStatus -eq '0xc000006a') {
            [pscustomobject]@{
                Account = ($data | Where-Object Name -eq 'TargetUserName').'#text'
                Source  = ($data | Where-Object Name -eq 'IpAddress').'#text'
            }
        }
    } | Group-Object Account, Source | Sort-Object Count -Descending | Select-Object -First 10

# Once identified, confirm current account state before resetting anything
Get-ADUser -Identity jdoe -Properties LockedOut, BadLogonCount, LastBadPasswordAttempt, PasswordLastSet
Production note: Resist resetting a user’s password just to make the 4625 events stop. If a device is retrying a stale cached credential, a reset only produces a fresh wave of failures once the device reconnects with the old password still cached. Use the Source Network Address and, where available, device inventory to find and fix the actual source.

If the pattern turns out to be a genuine password-spray attempt — many accounts, one Status/Sub Status pair, one source, tight timing — treat it as a security event rather than a helpdesk ticket: block the source, force resets on any account that shows a matching successful 4624 afterward, and verify the account lockout policy engaged as configured.

How to Prevent It

Confirm the Logon and Account Lockout audit subcategories are enabled for failure auditing with auditpol /get /category:"Logon/Logoff" — this is the audit category 4625 belongs to. From there, alert on patterns rather than raw volume: many accounts failing with the same Sub Status from one source address in a short window, or one high-value account failing from a Source Network Address it has never used before.

Pair Logon Type with account sensitivity when building detections — a failed Type 4 or Type 5 logon attempt against a domain admin account is worth its own alert regardless of the failure code, since that account should rarely if ever be authenticating as a scheduled task or service. For the underlying authentication mechanics this event sits on top of, the Kerberos authentication post covers what a successful exchange looks like, and the Event 4771 post covers the Kerberos-specific counterpart that fires before a logon session is even attempted.

Quick Reference

Code / Command Meaning / Use
0xC000006DGeneric invalid logon; check Sub Status for the real reason.
0xC0000064Sub Status: the account does not exist.
0xC000006ASub Status: the account exists but the password is wrong.
0xC0000234Status: account locked out.
0xC0000072Status: account disabled.
0xC0000193Status: account expired.
0xC0000071Status: password expired.
0xC0000224Status: password must be changed at next logon.
0xC000005EStatus: no logon servers available to service the request.
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4625}Pull 4625 events from the Security log for triage.
Search-ADAccount -LockedOutList every currently locked-out account in one query.
auditpol /get /category:"Logon/Logoff"Confirm the Logon and Account Lockout subcategories are auditing failures.

Final Thoughts

Event 4625 looks like a single, undifferentiated stream of failures until you separate it by Status, Sub Status, and Logon Type. Once it is, the same raw count of “failed logons this hour” turns into a small set of distinct problems: a stale credential, an intentionally disabled account, a genuinely unavailable domain controller, or an actual attacker — each needing a different response.

Read the Sub Status before the Status when the outer code is the generic 0xC000006D, and read the Logon Type before deciding how worried to be. A failed Type 2 at someone’s own desk is routine; the same failure code as a Type 3 against a service account from an address it has never used is not.

Key takeaway: Group your 4625 events by Status and Sub Status before reacting to volume. 0xC000006D with Sub Status 0xC000006A is a bad password; 0xC0000234 is a lockout worth confirming before unlocking; 0xC000005E means go check domain controller availability, not the account.
More troubleshooting

Next: NTLM authentication and how a restricted NTLM policy can turn ordinary logons into a wall of Event 4625 entries that have nothing to do with a wrong password at all.