derailed/k9s · warning · cordonErr

node is cordoned

Error message

node is cordoned

What it means

Sentinel error `cordonErr` in the k9s node renderer. `Node.diagnose` (internal/render/node.go:186) builds the status token list via `status(conds, node.Spec.Unschedulable, ...)`, which appends "SchedulingDisabled" when `spec.unschedulable` is true (node was cordoned via `kubectl cordon`). If the node is Ready but cordoned, diagnose returns this error; it is rendered into the VALID column and the row is colored with PendingColor, and it also flows into the view's health check. It is a status signal, not a program failure.

Source

Thrown at internal/render/node.go:35

	"github.com/derailed/k9s/internal/model1"
	"github.com/derailed/k9s/internal/slogs"
	"github.com/derailed/tcell/v2"
	"github.com/derailed/tview"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)

const (
	labelNodeRolePrefix = "node-role.kubernetes.io/"
	labelNodeRoleSuffix = "kubernetes.io/role"
)

var (
	cordonErr   = errors.New("node is cordoned")
	notReadyErr = errors.New("node is not ready")
)

var defaultNOHeader = model1.Header{
	model1.HeaderColumn{Name: "NAME"},
	model1.HeaderColumn{Name: "STATUS"},
	model1.HeaderColumn{Name: "ROLE"},
	model1.HeaderColumn{Name: "ARCH", Attrs: model1.Attrs{Wide: true}},
	model1.HeaderColumn{Name: "TAINTS"},
	model1.HeaderColumn{Name: "VERSION"},
	model1.HeaderColumn{Name: "OS-IMAGE", Attrs: model1.Attrs{Wide: true}},
	model1.HeaderColumn{Name: "KERNEL", Attrs: model1.Attrs{Wide: true}},
	model1.HeaderColumn{Name: "INTERNAL-IP", Attrs: model1.Attrs{Wide: true}},
	model1.HeaderColumn{Name: "EXTERNAL-IP", Attrs: model1.Attrs{Wide: true}},
	model1.HeaderColumn{Name: "PODS", Attrs: model1.Attrs{Align: tview.AlignRight}},
	model1.HeaderColumn{Name: "CPU", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
	model1.HeaderColumn{Name: "CPU/A", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},
	model1.HeaderColumn{Name: "%CPU", Attrs: model1.Attrs{Align: tview.AlignRight, MX: true}},

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Confirm the cordon is intentional (maintenance, drain in progress); the node still runs existing pods, it only stops accepting new ones
  2. Uncordon it: `kubectl uncordon <node-name>`, then refresh the node view
  3. If scheduling must stay disabled, treat the message as informational — it does not mean the node is broken
  4. Check why the node was cordoned: `kubectl describe node <name>` and look at taints/events if you did not cordon it yourself

Example fix

# before
kubectl cordon worker-1   # node shows: node is cordoned
# after
kubectl uncordon worker-1  # VALID column clears after refresh
Defensive patterns

Strategy: validation

Validate before calling

// Before acting on node health, check the cordon flag directly:
unschedulable, _ := unstructured.NestedBool(node.Object, "spec", "unschedulable")
if unschedulable {
    // treat as cordoned: informational, not a failure
}

Type guard

func isCordoned(n *v1.Node) bool {
    return n != nil && n.Spec.Unschedulable
}

Try / catch

if err := nodeRenderer.Healthy(ctx, obj); err != nil {
    if errors.Is(err, render.CordonErr) { /* log/ignore: node is cordoned */ }
    // handle other errors
}

Prevention

When it happens

Trigger: Calling Node.Render/Node.Healthy on a node whose `spec.unschedulable` is true and whose Ready condition is True: the STATUS column contains "SchedulingDisabled" and diagnose() takes the `cordoned` branch (internal/render/node.go:210).

Common situations: Operator ran `kubectl cordon` before maintenance or node drain; cluster autoscaler marked the node unschedulable; k9s node view shows VALID="node is cordoned" on a yellow row and the flash/health indicator reports it.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/d5a2743eb02c9d17. Report an issue: GitHub.