k3s-io/k3s · error
not authorized
Error message
not authorized
What it means
Thrown by doAuth in pkg/server/auth/auth.go when the HasRole/IsLocalOrHasRole middleware runs but the *config.Control pointer passed to the router is nil. k3s refuses to authorize the request because there is no server configuration at all, and returns HTTP 401 with body 'not authorized'. This is a wiring/startup-order defect, not a credential problem.
Source
Thrown at pkg/server/auth/auth.go:43
)
func hasRole(mustRoles []string, roles []string) bool {
for _, check := range roles {
for _, role := range mustRoles {
if role == check {
return true
}
}
}
return false
}
// doAuth calls the cluster's authenticator to validate that the client has at least one of the listed roles
func doAuth(roles []string, serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) {
switch {
case serverConfig == nil:
logrus.Errorf("Authenticate not initialized: serverConfig is nil")
util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
return
case serverConfig.Runtime.Authenticator == nil:
logrus.Errorf("Authenticate not initialized: serverConfig.Runtime.Authenticator is nil")
util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
return
}
resp, ok, err := serverConfig.Runtime.Authenticator.AuthenticateRequest(req)
if err != nil {
logrus.Errorf("Failed to authenticate request from %s: %v", req.RemoteAddr, err)
util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
return
}
if !ok || !hasRole(roles, resp.User.GetGroups()) {
util.SendError(errors.New("forbidden"), rw, req, http.StatusForbidden)
return
}View on GitHub (pinned to 6ba341e396)
Solutions
- Check the k3s server log for 'Authenticate not initialized: serverConfig is nil' - it identifies this exact case.
- If you embed or fork k3s, make sure the value passed to auth.HasRole(...) / handlers.Register is a non-nil *config.Control before the mux starts serving.
- If you are a normal client, wait for the server to fully start (poll /v1-k3s/readyz or the readyz endpoint) before calling protected endpoints.
- Restart the server process if it was started with a broken/absent configuration.
Example fix
// before (fork/embedding): router with nil config
mux.Handle("/v1-k3s/runtime", auth.HasRole(nil, "system:masters")(h))
// after: pass the initialized control config
mux.Handle("/v1-k3s/runtime", auth.HasRole(control, "system:masters")(h)) Defensive patterns
Strategy: validation
Validate before calling
// Before serving, ensure the control config is wired (embedding/tests)
func routerReady(control *config.Control) error {
if control == nil {
return errors.New("control config is nil; do not mount auth.HasRole routes")
}
return nil
} Prevention
- Poll /v1-k3s/readyz (or readyz) before calling role-protected supervisor endpoints.
- In forks/tests, never construct auth.HasRole(nil, ...); assert control != nil in a startup gate.
- Watch server logs for 'Authenticate not initialized' to catch wiring problems early.
When it happens
Trigger: An HTTP request reaches a role-protected supervisor endpoint (routes built with auth.HasRole / auth.IsLocalOrHasRole, e.g. the /v1-k3s runtime endpoints) while the serverConfig handed to handlers.Register is nil. Happens in custom builds that mount the k3s router with a nil control config, in unit tests that exercise the middleware without config, or if a request slips in before server setup assigns the config.
Common situations: Embedding k3s handlers in a test harness or fork; racing a request against server startup; refactors that construct routers before config.Control exists.
Related errors
- no bootstrap data is available to reconcile against
- etcd datastore disabled
- hijacking not supported
- server node name not set
- failed to get etcd MemberList: etcd not started
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/5faa47133f6bf2aa.
Report an issue: GitHub.