kubernetes/kubernetes · warning
PVC has empty pvc.Spec.VolumeName
Error message
PVC has empty pvc.Spec.VolumeName
What it means
Returned by getPVCExtractPV when a PVC is marked Bound but its Spec.VolumeName is empty. A bound claim must reference a PV name; an empty one is corrupt/inconsistent state that the volume manager cannot resolve to a PV.
Source
Thrown at pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go:553
// that can happen is that such pod is scheduled. This was the default
// behavior in 1.8 and earlier and users should not be that surprised.
// It should happen only in very rare case when scheduler schedules
// a pod and user deletes a PVC that's used by it at the same time.
//
// 2) Adding a check for kubernetes.io/pvc-protection here to prevent
// the existing running pods from being affected during the rebuild of
// the desired state of the world cache when the kubelet is restarted.
// It is safe for kubelet to add this check here because the PVC will
// be stuck in Terminating state until the pod is deleted.
if pvc.ObjectMeta.DeletionTimestamp != nil && !slices.Contains(pvc.Finalizers, util.PVCProtectionFinalizer) {
return nil, errors.New("PVC is being deleted")
}
if pvc.Status.Phase != v1.ClaimBound {
return nil, errors.New("PVC is not bound")
}
if pvc.Spec.VolumeName == "" {
return nil, errors.New("PVC has empty pvc.Spec.VolumeName")
}
return pvc, nil
}
// getPVSpec fetches the PV object with the given name from the API server
// and returns a volume.Spec representing it.
// An error is returned if the call to fetch the PV object fails.
func (dswp *desiredStateOfWorldPopulator) getPVSpec(
ctx context.Context,
name string,
pvcReadOnly bool,
expectedClaimUID types.UID) (*volume.Spec, string, error) {
pv, err := dswp.kubeClient.CoreV1().PersistentVolumes().Get(ctx, name, metav1.GetOptions{})
if err != nil || pv == nil {
return nil, "", fmt.Errorf(
"failed to fetch PV %s from API server: %v", name, err)
}View on GitHub (pinned to b882c60b40)
Solutions
- Inspect the PVC: kubectl get pvc <name> -o yaml; confirm spec.volumeName is truly empty.
- Delete and recreate the PVC so the binder/provisioner reconciles it cleanly.
- Check the persistent-volume-controller and provisioner logs for the binding failure.
- If a PV exists, manually set the claim's spec.volumeName (advanced) or recreate both.
Defensive patterns
Strategy: validation
Validate before calling
// Validate a bound PVC actually references a PV name.
func pvcHasVolume(pvc *corev1.PersistentVolumeClaim) bool {
return pvc.Status.Phase == corev1.ClaimBound && pvc.Spec.VolumeName != ""
} Type guard
func isPVCEmptyVolumeName(err error) bool {
return err != nil && err.Error() == "PVC has empty pvc.Spec.VolumeName"
} Try / catch
// This is corrupt state, not transient. Recreate the PVC rather than retrying blindly.
if _, err := getPVCExtractPV(...); err != nil {
if isPVCEmptyVolumeName(err) { /* recreate PVC; investigate binder */ }
} Prevention
- Treat Bound+empty VolumeName as data corruption; recreate the claim.
- Keep the PV controller / external-provisioner healthy.
- Avoid manually patching PVC phase to Bound.
When it happens
Trigger: Returned at desired_state_of_world_populator.go:553 when pvc.Spec.VolumeName == "". Hit after the Bound-phase check passes.
Common situations: Corrupted PVC object (controller bug, failed binding mid-write), an external provisioner that set phase=Bound without setting VolumeName, or recovery from a bad etcd state.
Related errors
- PVC is being deleted
- PVC is not bound
- no kubelet component config found
- no kubelet component config found in the active component co
- no client provided, cannot use webhook authentication
AI-assisted analysis of kubernetes/kubernetes@b882c60b40 (2026-08-07).
Data as JSON: /api/errors/863b0c2452774add.
Report an issue: GitHub.