tailscale/tailscale · error
LsaLogonUser(%q): %w, SubStatus: %v
Error message
LsaLogonUser(%q): %w, SubStatus: %v
What it means
The core LsaLogonUser S4U (service-for-user) call failed. The message carries both the primary NTSTATUS and SubStatus: the primary covers STATUS_LOGON_FAILURE / STATUS_ACCOUNT_RESTRICTION style failures, while SubStatus often pins the account-level reason (STATUS_ACCOUNT_DISABLED, STATUS_PASSWORD_EXPIRED, STATUS_INVALID_LOGON_HOURS, Kerberos KDC errors). LsaLogonUser itself requires the caller to hold SeTcbPrivilege.
Source
Thrown at util/winutil/s4u/lsa_windows.go:344
var srcContext _TOKEN_SOURCE
copy(srcContext.SourceName[:], []byte(srcName))
if err := allocateLocallyUniqueId(&srcContext.SourceIdentifier); err != nil {
return 0, err
}
originName, err := windows.NewNTString(srcName)
if err != nil {
return 0, err
}
var profileBuf uintptr
var profileBufLen uint32
var logonID windows.LUID
var quotas _QUOTA_LIMITS
var subNTStatus windows.NTStatus
ntStatus := lsaLogonUser(ls.handle, originName, _Network, pkgID, authInfo, authInfoLen, nil, &srcContext, &profileBuf, &profileBufLen, &logonID, &token, "as, &subNTStatus)
if e := wingoes.ErrorFromNTStatus(ntStatus); e.Failed() {
return 0, fmt.Errorf("LsaLogonUser(%q): %w, SubStatus: %v", u.Username, e, subNTStatus)
}
if profileBuf != 0 {
lsaFreeReturnBuffer(profileBuf)
}
return token, nil
}
// samToUPN16 converts SAM-style account name samName to a UPN account name,
// returned as a UTF-16 slice.
func samToUPN16(samName string) (upn16 []uint16, err error) {
_, samAccount, hasSep := strings.Cut(samName, `\`)
if !hasSep {
return nil, fmt.Errorf("%w: expected samName to contain a backslash", os.ErrInvalid)
}
// This is essentially the same algorithm used by Win32-OpenSSH:
// First, try obtaining a UPN directly...
upn16, err = translateName(samName, windows.NameSamCompatible, windows.NameUserPrincipal)View on GitHub (pinned to 6e0912f979)
Solutions
- Read SubStatus in the message first: it distinguishes account state from infrastructure failure
- Verify the account state (enabled, not locked, not expired) in AD or the SAM
- Check KDC/DC reachability and time skew (Kerberos is time-sensitive)
- Confirm the calling process runs as SYSTEM with SeTcbPrivilege
Example fix
// before
sess, err := s4u.Login(logf, srcName, u, s4u.CapCreateProcess)
if err != nil { return err } // opaque
// after
sess, err := s4u.Login(logf, srcName, u, s4u.CapCreateProcess)
if err != nil {
if strings.Contains(err.Error(), "STATUS_ACCOUNT_DISABLED") {
return errors.New("account disabled; contact IT")
}
if strings.Contains(err.Error(), "STATUS_INVALID_LOGON_HOURS") {
return errors.New("outside allowed logon hours")
}
return err
} Defensive patterns
Strategy: try-catch
Try / catch
sess, err := s4u.Login(logf, srcName, u, capLevel)
if err != nil {
msg := err.Error()
switch {
case strings.Contains(msg, "STATUS_ACCOUNT_DISABLED"):
return errors.New("account disabled")
case strings.Contains(msg, "STATUS_PASSWORD_EXPIRED"):
return errors.New("account password expired")
case strings.Contains(msg, "STATUS_INVALID_LOGON_HOURS"):
return errors.New("outside permitted logon hours")
}
return err // includes primary NTSTATUS and SubStatus for diagnosis
} Prevention
- Always read the SubStatus field in the message before guessing a cause
- Run the calling service as SYSTEM so SeTcbPrivilege is present
- Monitor DC health and clock skew on hosts performing S4U logons
When it happens
Trigger: Target account disabled, locked out, or expired; logon-hours policy violated when capLevel == CapCreateProcess; Kerberos S4U refused because the KDC is unreachable or the computer account is broken; caller thread missing SeTcbPrivilege.
Common situations: SSH pre-auth group lookups (ListGroupIDsForSSHPreAuthOnly) and launching processes as arbitrary users; service-account policy changes by IT; DC outages; using a Session from a non-SYSTEM context.
Related errors
- %q must be ASCII but contains value 0x%02X
- %w, actual length is %d
- %w: cannot logon as domain user without being joined to a do
- %w: %v
- samToUPN16: %w
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/4a7b4efee648be35.
Report an issue: GitHub.