tailscale/tailscale · error · ErrBadSrcName
%w, actual length is %d
Error message
%w, actual length is %d
What it means
logonAs rejects a token source name whose length is 0 or greater than _TOKEN_SOURCE_LENGTH (8), the fixed size of TOKEN_SOURCE.SourceName. The error wraps the ErrBadSrcName sentinel and reports the actual length, so errors.Is(err, ErrBadSrcName) detects it.
Source
Thrown at util/winutil/s4u/lsa_windows.go:260
if err != nil {
return username, false, err
}
if strings.EqualFold(before, comp) {
return after, false, nil
}
return username, true, nil
}
// logonAs performs a S4U logon for u on behalf of srcName, and returns an
// access token for the user if successful. srcName must be non-empty, ASCII,
// and no more than 8 characters long. If srcName does not meet this criteria,
// LogonAs will return ErrBadSrcName wrapped with additional information; use
// errors.Is to check for it. When capLevel == CapCreateProcess, the logon
// enforces the user's logon hours policy (when present).
func (ls *lsaSession) logonAs(srcName string, u *user.User, capLevel CapabilityLevel) (token windows.Token, err error) {
if ln := len(srcName); ln == 0 || ln > _TOKEN_SOURCE_LENGTH {
return 0, fmt.Errorf("%w, actual length is %d", ErrBadSrcName, ln)
}
if err := checkASCII(srcName); err != nil {
return 0, fmt.Errorf("%w: %v", ErrBadSrcName, err)
}
sanitizedUserName, isDomainUser, err := checkDomainAccount(u.Username)
if err != nil {
return 0, err
}
if isDomainUser && !winenv.IsDomainJoined() {
return 0, fmt.Errorf("%w: cannot logon as domain user without being joined to a domain", os.ErrInvalid)
}
var pkgID uint32
var authInfo unsafe.Pointer
var authInfoLen uint32
enforceLogonHours := capLevel == CapCreateProcess
if isDomainUser {View on GitHub (pinned to 6e0912f979)
Solutions
- Use a constant source name that is 1-8 ASCII characters (e.g. "tssvc")
- Validate length at config load time so the failure surfaces before any LSA work
- Handle ErrBadSrcName with errors.Is to give a precise error message
Example fix
// before
srcName := "my-long-service-name" // 20 chars
// after
const srcName = "tssvc" // <= 8 chars, ASCII
// or: if len(srcName) == 0 || len(srcName) > 8 { return cfgError } Defensive patterns
Strategy: validation
Validate before calling
const maxSrcName = 8 // _TOKEN_SOURCE_LENGTH
if len(srcName) == 0 || len(srcName) > maxSrcName {
return fmt.Errorf("srcName must be 1-%d ASCII characters", maxSrcName)
} Try / catch
sess, err := s4u.Login(logf, srcName, u, capLevel)
if err != nil {
if errors.Is(err, s4u.ErrBadSrcName) {
return fmt.Errorf("bad srcName (len=%d); use 1-8 ASCII chars", len(srcName))
}
return err
} Prevention
- Reserve a short fixed tag (e.g. "tssvc") as the source name
- Validate source names at config load, not at logon time
- Remember TOKEN_SOURCE.SourceName is 8 bytes - the limit is structural
When it happens
Trigger: Calling s4u.Login / ListGroupIDsForSSHPreAuthOnly with an empty srcName, or with a long human-readable service name (9+ characters) instead of a short source tag.
Common situations: Passing service display names ("My Company Agent") where an 8-char tag is required; defaulting the argument to the empty string when config is missing.
Related errors
- %q must be ASCII but contains value 0x%02X
- LsaLogonUser(%q): %w, SubStatus: %v
- %w: %v
- %w: cannot logon as domain user without being joined to a do
- samToUPN16: %w
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/220f552c52281b8f.
Report an issue: GitHub.