{"record":{"id":"da671ce015842931","repo":"derailed/k9s","slug":"expecting-a-maintainer-for-q","errorCode":null,"errorMessage":"expecting a maintainer for %q","messagePattern":"expecting a maintainer for %q","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/view/node.go","lineNumber":117,"sourceCode":"\n\topts := dao.DrainOptions{\n\t\tGracePeriodSeconds: -1,\n\t\tTimeout:            5 * time.Second,\n\t}\n\tShowDrain(n, sels, opts, drainNode)\n\n\treturn nil\n}\n\nfunc drainNode(v ResourceViewer, sels []string, opts dao.DrainOptions) {\n\tres, err := dao.AccessorFor(v.App().factory, v.GVR())\n\tif err != nil {\n\t\tv.App().Flash().Err(err)\n\t\treturn\n\t}\n\tm, ok := res.(dao.NodeMaintainer)\n\tif !ok {\n\t\tv.App().Flash().Err(fmt.Errorf(\"expecting a maintainer for %q\", v.GVR()))\n\t\treturn\n\t}\n\n\tv.Stop()\n\tdefer v.Start()\n\t{\n\t\td := NewDetails(v.App(), \"Drain Progress\", \"nodes\", contentYAML, true)\n\t\tif err := v.App().inject(d, false); err != nil {\n\t\t\tv.App().Flash().Err(err)\n\t\t}\n\t\tfor _, sel := range sels {\n\t\t\tif err := m.Drain(sel, opts, d.GetWriter()); err != nil {\n\t\t\t\tv.App().Flash().Err(err)\n\t\t\t}\n\t\t}\n\t\tv.Refresh()\n\t}\n}","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/view/node.go#L99-L135","documentation":"drainNode() fetches the accessor for the viewer's GVR and asserts it to dao.NodeMaintainer (the Drain-capable interface implemented by the Node DAO). Failure means the current GVR is not backed by the Node DAO, so draining is impossible — an invariant break rather than a cluster problem (a real drain failure would come from m.Drain instead).","triggerScenarios":"Invoking drain/cordon-adjacent actions while the viewer's GVR resolves to a non-Node DAO — a plugin or custom view rebinding the node viewer, or accessor registration skew after mixing k9s versions.","commonSituations":"Custom plugins wrapping the node view; k9s upgraded in place leaving stale compiled-in registrations; aliased GVRs for node-like CRDs using the node viewer.","solutions":["Run drain from the stock node view (:nodes / :v1:nodes)","Check views.yaml and plugins for anything rebinding the node viewer's GVR","Reinstall/upgrade k9s cleanly so view and dao packages match"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isNodeMaintainer(res dao.Resource) (dao.NodeMaintainer, bool) {\n    m, ok := res.(dao.NodeMaintainer)\n    return m, ok\n}\n\nif m, ok := isNodeMaintainer(res); !ok {\n    v.App().Flash().Errf(\"drain unsupported for %q\", v.GVR())\n    return\n} else {\n    defer func() { _ = m.Drain(sel, opts, w) }()\n}","tryCatchPattern":null,"preventionTips":["Expose drain only on viewers backed by the Node DAO","Assert interfaces with the ok form and bail with a clear message instead of proceeding","Keep node-related plugins compiled against the same k9s release"],"tags":["kubernetes","k9s","node","drain","type-assertion"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}