Dev.to · 4 min read

kubectl wait for condition fails on null conditions

kubectl wait for condition fails on null conditions

kubectl wait --for condition=Established crd/ is the usual way to wait for a new CRD before applying resources of its kind. It waits while the object has no status.conditions. It does not wait when status.conditions is present and null: it exits at once with an error. The kadet-hub/recipes CI hit that on 26 October 2026, with kubectl 1.37.1 against a kind node on Kubernetes 1.37.0: error: kubectl --kubeconfig ... wait --for condition=Established --timeout 60s crd/rabbitmqclusters.rabbitmq.com exited 1 error: .status.conditions accessor error: is of the type , expected []interface{} A rerun of the same job passed. The 60 second timeout never came into play. Why kubectl gives up kubectl reads the conditions with unstructured.NestedSlice in checkCondition (staging/src/k8s.io/kubectl/pkg/cmd/wait/condition.go, v1.37.2). NestedSlice has three outcomes. A missing field returns "not found", and checkCondition keeps waiting. A list is compared with the condition. Any other value, null included, is an error, and kubectl wait returns it without retrying: conditions, found, err := unstructured.NestedSlice(obj.Object, "status", "conditions") if err != nil { return false, err } if !found { return false, nil } The message is the one NestedSlice builds in apimachinery's helpers.go: %v accessor error: %v is of the type %T, expected []interface{}. Reproducing it on kind The CI failure is a race that we could not trigger on purpose. 200 rounds of kubectl create of a new CRD followed at once by kubectl wait --for condition=Established all passed, with kubectl 1.37.2 on a kind 0.33.0 cluster running Kubernetes 1.37.0. We have not found which write leaves a new CRD's conditions null for a moment. The code path itself is easy to reach with any object whose status.conditions is null. A CRD that keeps unknown fields lets you create one: apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: gadgets.example.com spec: group: example.com names: {kind: Gadget, plural: gadgets} scope: Namespaced versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object x-kubernetes-preserve-unknown-fields: true Create the object with kubectl create. kubectl apply drops the null, because in a merge patch null means "delete this field", and the stored status ends up as {}. $ printf 'apiVersion: example.com/v1\nkind: Gadget\nmetadata: {name: with-null}\nstatus: {conditions: null}\n' | kubectl create -f - gadget.example.com/with-null created $ kubectl get gadget with-null -o jsonpath='{.status}' {"conditions":null} $ kubectl wait --for condition=Ready --timeout=10s gadget/with-null error: .status.conditions accessor error: is of the type , expected []interface{} That returned after 0 seconds with exit code 1. A second Gadget without a status waited the full 10 seconds and then reported timed out waiting for the condition, which is the behaviour the CI job relied on. The JSONPath form does not help: $ kubectl wait --for=jsonpath='{.status.conditions[?(@.type=="Ready")].status}'=True --timeout=10s gadget/with-null error: is not array or slice and cannot be filtered It also exits after 0 seconds. A wait that holds Polling with kubectl get and retrying on any answer that is not True treats null, a missing field and a transient API error alike: timeout 60 bash -c 'until kubectl get crd/rabbitmqclusters.rabbitmq.com \ -o jsonpath="{.status.conditions[?(@.type==\"Established\")].status}" 2>/dev/null \ | grep -qx True; do sleep 1; done' Against the Gadget above, created with null conditions and patched to Ready=True three seconds later, the same loop with gadget/with-null and Ready returned 0 after 3 seconds. timeout turns a CRD that never becomes Established into exit code 124, so the job still fails on a real problem. The recipe check of kadet-hub does the same in Python since kadet-hub/recipes#49: it reads every new CRD with kubectl get -o json once a second and accepts it when an Established condition has status True, for up to 60 seconds. That replaced its three kubectl wait --for condition=Established calls. Originally published at kadet-hub.org.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News