GoogleContainerTools/skaffold · error

%s: 0/%d nodes available: %s

Error message

%s: 0/%d nodes available: %s

What it means

Aggregated scheduling-failure error listing why each candidate node was rejected: '0/N nodes available'. It carries code STATUSCHECK_UNKNOWN_UNSCHEDULABLE or a more specific taint-derived code (e.g. node network unavailable). Skaffold derives one message per matched taint/reason from the scheduler event.

Source

Thrown at pkg/diag/validator/validator.go:274

			}
		case v1.TaintNodeUnreachable:
			messages[i] = "1 node is unreachable"
			if errCode == proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE {
				errCode = proto.StatusCode_STATUSCHECK_NODE_UNREACHABLE
			}
		case v1.TaintNodeUnschedulable:
			messages[i] = "1 node is unschedulable"
			if errCode == proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE {
				errCode = proto.StatusCode_STATUSCHECK_NODE_UNSCHEDULABLE
			}
		case v1.TaintNodeNetworkUnavailable:
			messages[i] = "1 node's network not available"
			if errCode == proto.StatusCode_STATUSCHECK_UNKNOWN_UNSCHEDULABLE {
				errCode = proto.StatusCode_STATUSCHECK_NODE_NETWORK_UNAVAILABLE
			}
		}
	}
	return errCode, fmt.Errorf("%s: 0/%d nodes available: %s", reason, len(messages), strings.Join(messages, ", "))
}

func processPodEvents(e corev1.EventInterface, pod v1.Pod, ps *podStatus) *podStatus {
	updated := ps
	if _, ok := unknownConditionsOrSuccess[ps.ae.ErrCode]; !ok {
		return updated
	}
	log.Entry(context.TODO()).Debugf("Fetching events for pod %q", pod.Name)
	// Get pod events.
	scheme := runtime.NewScheme()
	scheme.AddKnownTypes(v1.SchemeGroupVersion, &pod)
	events, err := e.Search(scheme, &pod)
	if err != nil {
		log.Entry(context.TODO()).Debugf("Could not fetch events for resource %q due to %v", pod.Name, err)
		return updated
	}
	// find the latest event.
	var recentEvent *v1.Event

View on GitHub (pinned to a1189de023)

Solutions

  1. Read the per-node reasons in the message; for each, either fix the node condition or add a matching toleration to the pod.
  2. Uncordon unhealthy nodes: kubectl uncordon <node>, and fix node NotReady/network conditions.
  3. Add tolerations for legitimate persistent taints, e.g. tolerations for node.kubernetes.io/not-ready as appropriate.
  4. Scale the cluster (add nodes) if all nodes are genuinely full or tainted.

Example fix

// before: pod lacks toleration for unreachable node taint
// after
// tolerations:
// - key: node.kubernetes.io/network-unavailable
//   operator: Exists
//   effect: NoSchedule
Defensive patterns

Strategy: validation

Validate before calling

for _, n := range nodes.Items { for _, t := range n.Spec.Taints { if !tolerated(pod.Spec.Tolerations, t) { issues = append(issues, fmt.Sprintf("node %s tainted %s=%s", n.Name, t.Key, t.Effect)) } } }

Type guard

func allTaintsTolerated(nodes []v1.Node, tolerations []v1.Toleration) bool { for _, n := range nodes { for _, t := range n.Spec.Taints { if !tolerates(tolerations, t) { return false } } } return len(nodes) > 0 }

Try / catch

code, err := getUntoleratedTaints(reason, msg); if errors.Is(err, errUnschedulable) { for _, part := range strings.Split(msg, ": ") { log.Println("node reason:", part) } }

Prevention

When it happens

Trigger: getUntoleratedTaints matches taintsRe in the scheduler's failure message, builds a `messages` array from the matched taints/reasons, and returns fmt.Errorf("%s: 0/%d nodes available: %s", reason, len(messages), strings.Join(messages, ", ")).

Common situations: All nodes tainted (e.g. node.kubernetes.io/unreachable, not-ready, network unavailable) and the pod lacks tolerations; single-node clusters (minikube/kind) with the node tainted; cluster autoscaler not running while nodes are cordoned.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/cda3043d9f6443bb. Report an issue: GitHub.