k3s-io/k3s · error
failed to normalize server token; must be in format K10<CA-H
Error message
failed to normalize server token; must be in format K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>
What it means
NormalizeToken validates a server/node token with clientaccess.ParseUsernamePassword. Accepted shapes are the full bootstrap form 'K10<CA-HASH>::<USERNAME>:<PASSWORD>' or a bare '<PASSWORD>'. Any other shape — wrong segment count, missing '::' separator between CA hash and username, stray characters — returns this error with an empty password.
Source
Thrown at pkg/util/token.go:45
// try to use provided serverToken value, instead.
func ReadTokenFromFile(serverToken, certs, dataDir string) (string, error) {
tokenFile := filepath.Join(dataDir, "token")
b, err := os.ReadFile(tokenFile)
b = bytes.TrimSpace(b)
if os.IsNotExist(err) || len(b) == 0 {
return clientaccess.FormatToken(serverToken, certs)
}
return string(b), err
}
// NormalizeToken will normalize the token read from file or passed as a cli flag
func NormalizeToken(token string) (string, error) {
_, password, ok := clientaccess.ParseUsernamePassword(token)
if !ok {
return password, errors.New("failed to normalize server token; must be in format K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>")
}
return password, nil
}
func GetTokenHash(config *config.Control) (string, error) {
token := config.Token
if token == "" {
tokenFromFile, err := ReadTokenFromFile(config.Runtime.ServerToken, config.Runtime.ServerCA, config.DataDir)
if err != nil {
return "", err
}
token = tokenFromFile
}
normalizedToken, err := NormalizeToken(token)
if err != nil {
return "", err
}View on GitHub (pinned to 6ba341e396)
Solutions
- Use the exact format: K10<CA-HASH>::<USERNAME>:<PASSWORD>, or just the password portion alone
- Re-copy the token from /var/lib/rancher/k3s/server/token (or the node's node-token file) without truncation
- Trim whitespace and newlines when reading from a file: strings.TrimSpace(string(b))
- Verify the '::' separator sits between the CA hash and the username:password part
Example fix
// before token := "K10f2e0c1d4b7a::admin secret" // space instead of colon _, err := util.NormalizeToken(token) // after token := "K10f2e0c1d4b7a::admin:secret" _, err := util.NormalizeToken(strings.TrimSpace(token))
Defensive patterns
Strategy: validation
Validate before calling
var tokenRe = regexp.MustCompile(`^(K10[^:]+::[^:]+:.+|[^:]+)$`)
tok := strings.TrimSpace(rawToken)
if !tokenRe.MatchString(tok) {
return fmt.Errorf("token must be K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>")
}
_, err := util.NormalizeToken(tok) Try / catch
if _, err := util.NormalizeToken(tok); err != nil {
// surface the format error to the operator; never fall back to a default token
return fmt.Errorf("invalid token, re-copy from server token file: %w", err)
} Prevention
- Always TrimSpace (and strip CR/LF) on tokens read from files
- Automate token distribution from the server's token files instead of hand-copying
- Unit-test token parsing with the exact accepted shapes to catch format drift
When it happens
Trigger: Passing a token that is neither 'K10<hash>::<user>:<pass>' nor a single password segment: 'K10abc:admin:secret' (single colon instead of '::'), 'user:pass' alone, truncated pastes, or whitespace/CRLF-padded values read from a token file.
Common situations: Copy/paste truncation when moving tokens between nodes; hand-editing /var/lib/rancher/k3s/server/token or node-token files; CRLF line endings in Windows-edited token files; using a kubeconfig token where a k3s token is expected.
Related errors
- server token not found
- VPN Error. Tailscale requires a JoinKey
- insufficient PSK bytes
- Flannel configuration not defined
- Initial server URL host is not defined for load balancer
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/710293b6e5cb10c6.
Report an issue: GitHub.