{"record":{"id":"0a72648564336753","repo":"derailed/k9s","slug":"did-not-phase-down-correctly-expecting-0-replicas","errorCode":null,"errorMessage":"did not phase down correctly expecting 0 replicas but got %d","messagePattern":"did not phase down correctly expecting 0 replicas but got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/render/rs.go","lineNumber":102,"sourceCode":"\t\trs.Name,\n\t\tcomputeVulScore(rs.Namespace, rs.Labels, &rs.Spec.Template.Spec),\n\t\tstrconv.Itoa(int(*rs.Spec.Replicas)),\n\t\tstrconv.Itoa(int(rs.Status.Replicas)),\n\t\tstrconv.Itoa(int(rs.Status.ReadyReplicas)),\n\t\tstrings.Join(cos, \",\"),\n\t\tstrings.Join(imgs, \",\"),\n\t\tmapToStr(rs.Labels),\n\t\tAsStatus(r.diagnose(&rs)),\n\t\tToAge(rs.GetCreationTimestamp()),\n\t}\n\n\treturn nil\n}\n\nfunc (ReplicaSet) diagnose(rs *appsv1.ReplicaSet) error {\n\tif rs.Status.Replicas != rs.Status.ReadyReplicas {\n\t\tif rs.Status.Replicas == 0 {\n\t\t\treturn fmt.Errorf(\"did not phase down correctly expecting 0 replicas but got %d\", rs.Status.ReadyReplicas)\n\t\t}\n\t\treturn fmt.Errorf(\"mismatch desired(%d) vs ready(%d)\", rs.Status.Replicas, rs.Status.ReadyReplicas)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":84,"sourceCodeEnd":109,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/render/rs.go#L84-L109","documentation":"ReplicaSet.diagnose is the VALID-column health check. When rs.Status.Replicas equals 0 but rs.Status.ReadyReplicas is non-zero — the controller reports zero total replicas while some pods still count as ready — it returns 'did not phase down correctly expecting 0 replicas but got N'. The message signals a scale-down whose status counters have not converged (typically pods still terminating or a stale status cache). It is rendered as status text via AsStatus and never returned to the caller as a failure.","triggerScenarios":"An RS scaled to 0 (or being deleted) while its pods are still terminating; reading the RS view right after kubectl scale rs/<n> --replicas=0; momentary informer staleness during heavy churn. Any row where status.replicas==0 && status.readyReplicas>0 trips it.","commonSituations":"Scaling to zero before removing a deployment; CI batches deleting workloads; autoscaler floors at 0; long terminationGracePeriodSeconds or finalizers keeping pods alive.","solutions":["Wait for terminating pods to finish: kubectl get pods -l <selector> until none remain","Check finalizers and terminationGracePeriodSeconds keeping pods terminating","Refresh the view — a stale informer cache can show the mismatch briefly","If it persists, kubectl describe rs <name> and inspect controller events"],"exampleFix":"# before: rs at 0 replicas, pods still terminating\nkubectl scale rs/myrs --replicas=0\nkubectl get pods -l app=myrs\n# after: wait for the ready counters to converge\nkubectl rollout status rs/myrs --timeout=60s","handlingStrategy":"validation","validationCode":"# catch the condition before it renders\nkubectl get rs -o json | jq '.items[] | select(.status.replicas==0 and .status.readyReplicas>0) | {name:.metadata.name, ready:.status.readyReplicas}'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wait for terminating pods to exit before reading RS health (kubectl rollout status rs/<name>)","Avoid overly long terminationGracePeriodSeconds and stray finalizers","Refresh before reacting: informer staleness can show the mismatch briefly","Read the VALID column as transient scale-down state, not a defect"],"tags":["kubernetes","workloads","replicaset","cluster-state","health","k9s"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}