{"record":{"id":"55aad027721eaf00","repo":"k3s-io/k3s","slug":"failed-to-start-wranger-controllers","errorCode":null,"errorMessage":"failed to start wranger controllers","messagePattern":"failed to start wranger controllers","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/server/server.go","lineNumber":193,"sourceCode":"\treturn nil\n}\n\n// apiserverControllers starts the core controllers, as well as the leader-elected controllers\n// that should only run on a control-plane node.\nfunc apiserverControllers(ctx context.Context, sc *Context, config *Config) {\n\tif err := coreControllers(ctx, sc, config); err != nil {\n\t\tpanic(err)\n\t}\n\tfor _, controller := range config.LeaderControllers {\n\t\tif err := controller(ctx, sc); err != nil {\n\t\t\tpanic(errors.WithMessagef(err, \"failed to start %s leader controller\", util.GetFunctionName(controller)))\n\t\t}\n\t}\n\n\t// Re-run informer factory startup after core and leader-elected controllers have started.\n\t// Additional caches may need to start for the newly added OnChange/OnRemove callbacks.\n\tif err := sc.Start(ctx); err != nil {\n\t\tpanic(errors.WithMessage(err, \"failed to start wranger controllers\"))\n\t}\n}\n\n// runOrDie is similar to leader.RunOrDie, except that it runs the callback\n// immediately, without performing leader election.\nfunc runOrDie(ctx context.Context, name string, cb leader.Callback) {\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\tlogrus.WithField(\"stack\", string(debug.Stack())).Fatalf(\"%s controller panic: %v\", name, err)\n\t\t}\n\t}()\n\tcb(ctx)\n\t<-ctx.Done()\n}\n\n// coreControllers starts the following controllers, if they are enabled:\n// * Node controller (manages coredns node hosts file)\n// * Helm controller","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/server/server.go#L175-L211","documentation":"k3s server (pkg/server/server.go, apiserverControllers): after leader controllers register additional OnChange/OnRemove callbacks, sc.Start(ctx) re-runs informer factory startup so newly needed caches begin syncing against the apiserver. This panic (message contains the 'wranger' typo) fires when that informer startup fails, i.e. the apiserver could not serve the watch/list requests the new caches issued.","triggerScenarios":"sc.Start racing an apiserver that is restarting, overloaded, or rejecting watches; informer caches for newly registered controllers failing on RBAC/authn errors; datastore (etcd/sqlite) too slow or unavailable behind the apiserver.","commonSituations":"Control-plane restarts and rolling upgrades where controllers start before the apiserver is fully ready; small/single-node servers under memory or disk pressure; misconfigured admission or auth plugins causing watch failures; this panic often appears together with 226's controller panics.","solutions":["Verify apiserver readiness (k3s kubectl get --raw /readyz) and scan preceding k3s log lines for apiserver or datastore errors","Relieve pressure: free disk, raise memory limits, and check embedded datastore (etcd/sqlite) health on the node","Restart k3s once the apiserver reports healthy so informer factory startup re-runs","On persistent failure, align k3s versions and review any apiserver flag overrides (--kube-apiserver-arg) that could break watch serving"],"exampleFix":"// before\nif err := sc.Start(ctx); err != nil {\n    panic(errors.WithMessage(err, \"failed to start wranger controllers\"))\n}\n\n// after (tolerate apiserver warmup with a bounded retry instead of panicking)\nif err := wait.PollUntilContextTimeout(ctx, 5*time.Second, 2*time.Minute, true,\n    func(ctx context.Context) (bool, error) {\n        return sc.Start(ctx) == nil, nil\n    }); err != nil {\n    panic(errors.WithMessage(err, \"failed to start wrangler controllers\"))\n}","handlingStrategy":"retry","validationCode":"// pre-check apiserver readiness before re-running informer factory startup\nif err := wait.PollUntilContextTimeout(ctx, time.Second, 2*time.Minute, true, func(ctx context.Context) (bool, error) {\n    resp, err := localhostClient().Get(\"https://127.0.0.1:6443/readyz\")\n    if err != nil {\n        return false, nil\n    }\n    defer resp.Body.Close()\n    return resp.StatusCode == http.StatusOK, nil\n}); err != nil {\n    return fmt.Errorf(\"apiserver not ready before informer restart: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sequence restarts: apiserver healthy, then controllers; avoid restarting all control-plane components simultaneously","Size control-plane nodes for apiserver + informer memory so watch establishment does not time out","Review --kube-apiserver-arg overrides after k3s upgrades; watch-serving regressions surface here first","Treat this panic (and its 'wranger' typo) as an apiserver-side symptom: investigate the apiserver, not the controllers"],"tags":["wrangler","informer","apiserver","panic","startup","k3s"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}