k3s-io/k3s · error

--token is required

Error message

--token is required

What it means

Agent bootstrap requires either a valid join token or an already-issued client-kubelet certificate pair from a previous join. The check first attempts tls.LoadX509KeyPair on <data-dir>/agent/client-kubelet.crt/key; only if those are missing/invalid AND no --token/--token-file was provided does it fail. So the error means: first run without a token, or a wiped data-dir on rejoin without re-supplying credentials.

Source

Thrown at pkg/cli/agent/agent.go:83

		if err := permissions.IsPrivileged(); err != nil {
			return errors.WithMessage(err, "agent requires additional privilege if not run with --rootless")
		}
	}

	if cmds.AgentConfig.TokenFile != "" {
		token, err := util.ReadFile(ctx, cmds.AgentConfig.TokenFile)
		if err != nil {
			return err
		}
		cmds.AgentConfig.Token = token
	}

	clientKubeletCert := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.crt")
	clientKubeletKey := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.key")
	_, err := tls.LoadX509KeyPair(clientKubeletCert, clientKubeletKey)

	if err != nil && cmds.AgentConfig.Token == "" {
		return errors.New("--token is required")
	}

	if cmds.AgentConfig.ServerURL == "" {
		return errors.New("--server is required")
	}

	if cmds.AgentConfig.FlannelIface != "" && len(cmds.AgentConfig.NodeIP.Value()) == 0 {
		ip, err := util.GetIPFromInterface(cmds.AgentConfig.FlannelIface)
		if err != nil {
			return err
		}
		cmds.AgentConfig.NodeIP.Set(ip)
	}

	logrus.Info("Starting " + version.Program + " agent " + clx.App.Version)

	dataDir, err := datadir.LocalHome(cmds.AgentConfig.DataDir, cmds.AgentConfig.Rootless)
	if err != nil {

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Get the token from the server (cat /var/lib/rancher/k3s/server/token) and pass it: k3s agent --token <token> --server https://<server>:6443
  2. Or persist it via --token-file / K3S_TOKEN_FILE so restarts don't depend on flags
  3. If this is a rejoin after wiping the data-dir, supply the token again — the old client cert no longer exists

Example fix

# before
k3s agent --server https://10.0.0.10:6443  # no token, no cached cert -> --token is required

# after
K3S_TOKEN=K10...::server:... k3s agent --server https://10.0.0.10:6443
Defensive patterns

Strategy: validation

Validate before calling

func hasBootstrapCreds(dataDir string, token string) error {
    cert := filepath.Join(dataDir, "agent", "client-kubelet.crt")
    key := filepath.Join(dataDir, "agent", "client-kubelet.key")
    if _, err := tls.LoadX509KeyPair(cert, key); err == nil { return nil } // cached creds OK
    if strings.TrimSpace(token) == "" { return errors.New("provide --token (from /var/lib/rancher/k3s/server/token) on first join") }
    return nil
}

Prevention

When it happens

Trigger: First `k3s agent` run with no --token, --token-file, or K3S_TOKEN; data-dir was deleted/corrupted so the cached kubelet client cert is gone and no token is passed; token file path pointing to an empty file that still yields empty Token.

Common situations: New node provisioning where the token secret was not distributed; rejoining after rm -rf /var/lib/rancher/k3s without updating the agent unit; token file with wrong permissions read as empty.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/7c46d11128d04380. Report an issue: GitHub.