{"record":{"id":"710293b6e5cb10c6","repo":"k3s-io/k3s","slug":"failed-to-normalize-server-token-must-be-in-forma","errorCode":null,"errorMessage":"failed to normalize server token; must be in format K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>","messagePattern":"failed to normalize server token; must be in format K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/token.go","lineNumber":45,"sourceCode":"// try to use provided serverToken value, instead.\nfunc ReadTokenFromFile(serverToken, certs, dataDir string) (string, error) {\n\ttokenFile := filepath.Join(dataDir, \"token\")\n\n\tb, err := os.ReadFile(tokenFile)\n\tb = bytes.TrimSpace(b)\n\n\tif os.IsNotExist(err) || len(b) == 0 {\n\t\treturn clientaccess.FormatToken(serverToken, certs)\n\t}\n\n\treturn string(b), err\n}\n\n// NormalizeToken will normalize the token read from file or passed as a cli flag\nfunc NormalizeToken(token string) (string, error) {\n\t_, password, ok := clientaccess.ParseUsernamePassword(token)\n\tif !ok {\n\t\treturn password, errors.New(\"failed to normalize server token; must be in format K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>\")\n\t}\n\n\treturn password, nil\n}\n\nfunc GetTokenHash(config *config.Control) (string, error) {\n\ttoken := config.Token\n\tif token == \"\" {\n\t\ttokenFromFile, err := ReadTokenFromFile(config.Runtime.ServerToken, config.Runtime.ServerCA, config.DataDir)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\ttoken = tokenFromFile\n\t}\n\tnormalizedToken, err := NormalizeToken(token)\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/util/token.go#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntoken := \"K10f2e0c1d4b7a::admin secret\" // space instead of colon\n_, err := util.NormalizeToken(token)\n\n// after\ntoken := \"K10f2e0c1d4b7a::admin:secret\"\n_, err := util.NormalizeToken(strings.TrimSpace(token))","handlingStrategy":"validation","validationCode":"var tokenRe = regexp.MustCompile(`^(K10[^:]+::[^:]+:.+|[^:]+)$`)\ntok := strings.TrimSpace(rawToken)\nif !tokenRe.MatchString(tok) {\n    return fmt.Errorf(\"token must be K10<CA-HASH>::<USERNAME>:<PASSWORD> or <PASSWORD>\")\n}\n_, err := util.NormalizeToken(tok)","typeGuard":null,"tryCatchPattern":"if _, err := util.NormalizeToken(tok); err != nil {\n    // surface the format error to the operator; never fall back to a default token\n    return fmt.Errorf(\"invalid token, re-copy from server token file: %w\", err)\n}","preventionTips":["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"],"tags":["auth","token","credentials","config"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}