derailed/k9s · warning
chart is in an invalid state
Error message
chart is in an invalid state
What it means
Chart.diagnose (helm view) returns this when a release's status is anything other than 'deployed' — e.g. pending-install, pending-upgrade, failed, uninstalled, superseded. Caveat: if the passed object is not *ReleaseRes the code logs the type mismatch but keeps going and dereferences a nil Release, which panics — always pass *ReleaseRes.
Source
Thrown at internal/render/helm/chart.go:86
render.ToAge(metav1.Time{Time: h.Release.Info.LastDeployed.Time}),
}
return nil
}
// Healthy checks component health.
func (c Chart) Healthy(_ context.Context, o any) error {
h, ok := o.(*ReleaseRes)
if !ok {
slog.Error("Expected *ReleaseRes, but got", slogs.Type, fmt.Sprintf("%T", o))
}
return c.diagnose(h.Release.Info.Status.String())
}
func (Chart) diagnose(s string) error {
if s != "deployed" {
return fmt.Errorf("chart is in an invalid state")
}
return nil
}
// ----------------------------------------------------------------------------
// Helpers...
// ReleaseRes represents a helm chart resource.
type ReleaseRes struct {
Release *release.Release
}
// GetObjectKind returns a schema object.
func (ReleaseRes) GetObjectKind() schema.ObjectKind {
return nil
}
View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Inspect state: helm status <release> -n <ns> and helm history <release> -n <ns>
- For failed/pending-upgrade: helm rollback <release> <good-revision> or fix values and helm upgrade again
- If truly stuck: helm uninstall then reinstall (data loss for stateful workloads — drain first)
- Ensure callers pass *ReleaseRes so the nil-deref path cannot trigger
Example fix
// before: passes the wrong type -> nil deref after the !ok log
chart.Healthy(ctx, res)
// after
if rr, ok := res.(*ReleaseRes); ok {
_ = chart.Healthy(ctx, rr)
} Defensive patterns
Strategy: type-guard
Validate before calling
st := h.Release.Info.Status.String()
if st != "deployed" {
// surface 'helm status <release>' instead of failing
} Type guard
func isReleaseRes(o any) (*ReleaseRes, bool) {
rr, ok := o.(*ReleaseRes)
return rr, ok
} Try / catch
Guard the type first (the library dereferences nil after a failed assertion); then map non-deployed statuses to remediation: rollback for failed, wait for pending.
Prevention
- Always pass *ReleaseRes to Chart.Healthy — wrong types panic, not error
- Run helm with --wait and adequate --timeout so releases settle before listing
- Roll back immediately on failed upgrades in CI to avoid superseded/failed clutter
When it happens
Trigger: helm install/upgrade interrupted (pending-upgrade); a release whose hooks or pods failed (failed); listing releases right after upgrade before settle; --wait releases still converging.
Common situations: CI pipelines killing helm mid-upgrade leaving pending states; chart hooks failing; rolling back (superseded); timeouts on slow clusters.
Related errors
- %s
- could not convert revision to a number: %w
- %s
- desiring %d replicas got %d available
- desiring %d replicas but %d ready
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/622d8321cb602da4.
Report an issue: GitHub.