{"record":{"id":"2b587177b7651d02","repo":"derailed/k9s","slug":"failed-to-delete-or-recycle","errorCode":null,"errorMessage":"failed to delete or recycle","messagePattern":"failed to delete or recycle","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/render/pv.go","lineNumber":131,"sourceCode":"\t\tsize.String(),\n\t\taccessMode(pv.Spec.AccessModes),\n\t\tstring(pv.Spec.PersistentVolumeReclaimPolicy),\n\t\tstring(phase),\n\t\tclaim,\n\t\tclass,\n\t\tpv.Status.Reason,\n\t\tp.volumeMode(pv.Spec.VolumeMode),\n\t\tmapToStr(pv.Labels),\n\t\tAsStatus(p.diagnose(phase)),\n\t\tToAge(pv.GetCreationTimestamp()),\n\t}\n\n\treturn nil\n}\n\nfunc (PersistentVolume) diagnose(phase v1.PersistentVolumePhase) error {\n\tif phase == v1.VolumeFailed {\n\t\treturn fmt.Errorf(\"failed to delete or recycle\")\n\t}\n\n\treturn nil\n}\n\nfunc (PersistentVolume) volumeMode(m *v1.PersistentVolumeMode) string {\n\tif m == nil {\n\t\treturn MissingValue\n\t}\n\n\treturn string(*m)\n}\n\n// ----------------------------------------------------------------------------\n// Helpers...\n\nfunc accessMode(aa []v1.PersistentVolumeAccessMode) string {\n\tdd := accessDedup(aa)","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/render/pv.go#L113-L149","documentation":"PersistentVolume.diagnose is the health check behind the VALID column: when a PV's status.phase equals v1.VolumeFailed it returns 'failed to delete or recycle', which AsStatus renders as row status text. It means the reclaim policy (Delete or Recycle) attempted to free the backing storage and the storage layer failed. The error describes cluster state, not a bug in the caller, and it never propagates out of Render.","triggerScenarios":"A PV with spec.persistentVolumeReclaimPolicy Delete or Recycle whose deletion at the CSI driver or cloud provider failed (driver errors, missing permissions, backing cloud already deleted); browsing the PV view in k9s while such a PV exists.","commonSituations":"Decommissioning a cluster while PVs still use Delete policy; broken CSI driver or cloud credentials; provisioning backends that do not implement deletion; orphaned PVs after the storage backend was removed.","solutions":["Run kubectl describe pv <name> and read Events to find why reclaim failed","Fix the CSI driver / cloud credentials so deletion succeeds and let the controller retry","Protect data first: kubectl patch pv <name> -p '{\"spec\":{\"persistentVolumeReclaimPolicy\":\"Retain\"}}' and clean up manually","If the PV is truly orphaned, remove stale finalizers (e.g. storage.kubernetes.io/pv-protection) only after verifying no data is at risk"],"exampleFix":"# before: PV stuck Failed with reclaimPolicy Delete\nkubectl get pv pvc-123 -o jsonpath='{.status.phase} {..persistentVolumeReclaimPolicy}'\n# Failed Delete\n# after: keep the data, stop the failed reclaim loop\nkubectl patch pv pvc-123 -p '{\"spec\":{\"persistentVolumeReclaimPolicy\":\"Retain\"}}'","handlingStrategy":"validation","validationCode":"# surface PVs that will trip the diagnostic before opening the view\nkubectl get pv -o json | jq '.items[] | select(.status.phase==\"Failed\") | {name:.metadata.name, policy:.spec.persistentVolumeReclaimPolicy}'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set persistentVolumeReclaimPolicy to Retain for volumes holding data you cannot lose","Keep the CSI driver and cloud credentials healthy so Delete reclaim succeeds","Watch PV events: a Failed phase is a storage-layer problem, not a UI error","Treat the VALID column text as a signal to investigate with kubectl describe, not an exception to catch"],"tags":["kubernetes","storage","persistent-volume","reclaim-policy","cluster-state","health"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}