RADIUS Log Analyzer

Login incorrect (...): [user] (from client ...)

FreeRADIUS 'Login incorrect' — how to read radiusd -X to find the real reason

The log line:

(8) Login incorrect (mschap: ERROR: MS-CHAP2-Response is incorrect): [alice] (from client office-ap port 0 via TLS tunnel)

What it actually means: almost nothing by itself — and that’s the point of this page. Login incorrect is FreeRADIUS’s summary line, printed for every deliberate reject regardless of cause. Treating it as the diagnosis is the most common dead end in FreeRADIUS troubleshooting. The actual reason is always earlier in the debug output; here’s how to walk to it.

1. Read the parentheses first

Since FreeRADIUS 3, the summary line includes the failing module’s own message in parentheses — (mschap: ERROR: ...), (pap: ...), (eap_peap: ...), (rlm_ldap: ...). That names the module and often the whole answer. Frequent parentheticals and where they lead:

Parenthetical containsReal problemDeep dive
mschap: ... MS-CHAP2-Response is incorrectwrong password / hash format / AD account stateMSCHAPv2 page
No "known good" passwordbackend never supplied a passwordknown-good password page
eap: ... EAP sub-module failedlook further up for the TLS alertunknown CA, expired cert
rlm_ldap / sql errorsbackend lookup or bind failureLDAP bind page

2. Walk up to the last authenticate section

Find the request number — the (8) prefix — and read only lines with that prefix (in a busy debug, interleaved requests are the great confuser; grep '^(8)' is your friend). Locate the authenticate { block and read which module returned reject or fail:

(8)     [mschap] = reject
(8)   } # authenticate = reject

Everything above that module’s own ERROR lines is context; the first ERROR from that module is the diagnosis.

3. If no module errored — it’s your policy

Sometimes nothing “failed”: an unlang policy deliberately rejected (a group check, a NAS filter, a time restriction). Search the request’s lines for Post-Auth-Type REJECT and for reject inside policy / authorize sections — the surrounding lines name the condition that fired.

The habit that makes this fast

Never debug from /var/log/radius/radius.log — its one-line summaries are exactly the trap this page describes. Reproduce the failure once under radiusd -X (or raddebug on a running server) and you get every decision the server made, in order. Then the reject is never mysterious: it’s the last module that said no, for the reason printed right above it.

Diagnose your actual log

Generic explanations only go so far. Paste your full log into the analyzer — it detects this failure and 18 others, ranks the likely causes for your specific output, and runs entirely in your browser. Nothing is uploaded.