k3s-io/k3s · error
token must not be empty
Error message
token must not be empty
What it means
clientaccess.parseToken (reached through ParseAndValidateToken / NewAccessInfo when joining a server or agent) rejects the empty string before doing any format work. It exists so callers fail fast with a clear message instead of proceeding with anonymous access.
Source
Thrown at pkg/clientaccess/token.go:216
// along with a bool indicating if the token was successfully parsed.
// Kubeadm-style tokens have ID/Secret not Username/Password and therefore will return false (invalid).
func ParseUsernamePassword(token string) (string, string, bool) {
info, err := parseToken(token)
if err != nil {
return "", "", false
}
if info.BootstrapTokenString != nil {
return "", "", false
}
return info.Username, info.Password, true
}
// parseToken parses a token into an Info struct
func parseToken(token string) (*Info, error) {
var info Info
if len(token) == 0 {
return nil, errors.New("token must not be empty")
}
// Turn bare password or bootstrap token into full K10 token with empty CA hash,
// for consistent parsing in the section below.
if !strings.HasPrefix(token, tokenPrefix) {
_, err := kubeadm.NewBootstrapTokenString(token)
if err != nil {
token = tokenPrefix + ":::" + token
} else {
token = tokenPrefix + "::" + token
}
}
// Strip off the prefix.
token = token[len(tokenPrefix):]
// Split into CA hash and creds.
parts := strings.SplitN(token, "::", 2)View on GitHub (pinned to 6ba341e396)
Solutions
- Provide a valid token: read it from the seed server's /var/lib/rancher/k3s/server/token or generate one with `k3s token create`
- Verify the env/secret actually carries a value: `[ -n "$K3S_TOKEN" ] && echo set`
- In Go callers, guard with a non-empty check before calling ParseAndValidateToken
Example fix
// before
info, err := clientaccess.ParseAndValidateToken(serverURL, os.Getenv("K3S_TOKEN"))
// after
token := strings.TrimSpace(os.Getenv("K3S_TOKEN"))
if token == "" {
return errors.New("K3S_TOKEN must be set when joining")
}
info, err := clientaccess.ParseAndValidateToken(serverURL, token) Defensive patterns
Strategy: validation
Validate before calling
token := strings.TrimSpace(cfg.Token)
if token == "" {
return fmt.Errorf("token must not be empty when joining %s", cfg.JoinURL)
}
info, err := clientaccess.ParseAndValidateToken(cfg.JoinURL, token) Type guard
func isNonEmptyToken(token string) bool { return strings.TrimSpace(token) != "" } Try / catch
// parseToken errors are plain strings; match on content only after non-nil check
info, err := clientaccess.ParseAndValidateToken(server, token)
if err != nil {
if strings.Contains(err.Error(), "token must not be empty") {
return errors.New("join token missing: check K3S_TOKEN/secret injection")
}
return err
} Prevention
- Gate startup on token presence before calling clientaccess APIs
- Mount tokens from secrets/files rather than trusting env inheritance
- Add readiness checks that fail fast when token secrets are empty
When it happens
Trigger: Calling clientaccess.ParseAndValidateToken(serverURL, "") in Go, or starting k3s with `--server <url>` while K3S_TOKEN/--token is empty (or the config `token:` key is an empty string).
Common situations: K3S_TOKEN exported but set to empty (`K3S_TOKEN=` in a unit file); config.yaml containing `token: ""`; secrets mounted for the token that are empty on first rollout.
Related errors
- invalid token CA hash length
- invalid token format
- only https:// URLs are supported, invalid scheme:
- invalid snapshot operation
- invalid output format: {cfg.Output}
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/f3bd6ea40a63092e.
Report an issue: GitHub.