derailed/k9s · warning

strings.Join(errs, " - ")

Error message

strings.Join(errs, " - ")

What it means

CustomResourceDefinition.diagnose accumulates per-version findings — deprecation warnings (or the generic '<name>[<ver>] is deprecated' when a deprecated version omits a warning) and 'CRD <n> is no longer served by the api server' when no version has Served=true — then joins them with ' - ' into one error. It is the CRD list's health column: each condition is advisory, describing deprecation/serving state rather than a k9s failure.

Source

Thrown at internal/render/crd.go:132

				ee = append(ee, fmt.Errorf("%s", *v.DeprecationWarning))
			} else {
				ee = append(ee, fmt.Errorf("%s[%s] is deprecated", n, v.Name))
			}
		}
	}
	if !served {
		ee = append(ee, fmt.Errorf("CRD %s is no longer served by the api server", n))
	}

	if len(ee) == 0 {
		return nil
	}
	errs := make([]string, 0, len(ee))
	for _, e := range ee {
		errs = append(errs, e.Error())
	}

	return errors.New(strings.Join(errs, " - "))
}

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. For deprecation messages: migrate manifests/clients off the deprecated version (the message names CRD and version, or carries the upstream deprecationWarning text).
  2. For 'no longer served': set Served:true on the intended version in the CRD spec (kubectl edit crd <name>) or remove the dead CRD.
  3. Treat the row as a warning signal — no k9s-side remediation is expected.

Example fix

# before
versions:
  - name: v1beta1
    served: false
    storage: true
# after
versions:
  - name: v1
    served: true
    storage: true
  - name: v1beta1
    served: false
    storage: false
    deprecated: true
    deprecationWarning: "use v1"
Defensive patterns

Strategy: validation

Validate before calling

func crdHealthy(name string, vv []v1.CustomResourceDefinitionVersion) bool {
    served := false
    for _, v := range vv {
        if v.Served { served = true }
        if v.Deprecated { return false } // deprecation surfaces in diagnose
    }
    return served && len(vv) > 0
}

Try / catch

if err := crdDiagnose(name, versions); err != nil {
    msg := err.Error() // dash-joined: parse segments to act per finding (deprecation vs not served)
    if strings.Contains(msg, "no longer served") { /* set Served:true or delete CRD */ }
}

Prevention

When it happens

Trigger: Viewing CRDs where at least one spec.versions[] entry has Deprecated:true (with or without deprecationWarning), or where the versions list has no entry with Served:true; multiple findings concatenate into a single dash-joined message.

Common situations: Operators deprecating v1beta1-style storage versions during upgrades; admins flipping Served:false during version migrations; CRDs installed with all versions unserved by mistake.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/ec148d5c4416ccc8. Report an issue: GitHub.