From 4366fdebf99ecaa3df1bce964818f4890f63794f Mon Sep 17 00:00:00 2001 From: immanuwell Date: Fri, 29 May 2026 19:59:42 +0400 Subject: [PATCH] Fix nil Reason panic in chartutil Honor the documented ErrUnknown fallback when ErrValuesReference has a nil Reason. Add a regression test for zero-value and wrapped-error cases so formatting the exported error type does not panic. Signed-off-by: immanuwell Assisted-by: codex/gpt-5 --- chartutil/values.go | 6 +++--- chartutil/values_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/chartutil/values.go b/chartutil/values.go index 5ccbc8acd..543cbe8a9 100644 --- a/chartutil/values.go +++ b/chartutil/values.go @@ -95,9 +95,9 @@ func (e *ErrValuesReference) Error() string { if key := e.Key; key != "" { b.WriteString(fmt.Sprintf(" with key '%s'", key)) } - reason := e.Reason.Error() - if reason == "" && e.Err == nil { - reason = ErrUnknown.Error() + reason := ErrUnknown.Error() + if e.Reason != nil && e.Reason.Error() != "" { + reason = e.Reason.Error() } if e.Err != nil { reason = e.Err.Error() diff --git a/chartutil/values_test.go b/chartutil/values_test.go index 00e2bd864..fca319d82 100644 --- a/chartutil/values_test.go +++ b/chartutil/values_test.go @@ -18,6 +18,7 @@ package chartutil import ( "context" + "errors" "testing" "github.com/go-logr/logr" @@ -26,6 +27,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/fluxcd/pkg/apis/meta" @@ -406,6 +408,38 @@ func TestReplacePathValue(t *testing.T) { } } +func TestErrValuesReferenceErrorNilReason(t *testing.T) { + tests := []struct { + name string + err *ErrValuesReference + want string + }{ + { + name: "without wrapped error", + err: &ErrValuesReference{ + Name: types.NamespacedName{Namespace: "default", Name: "values"}, + }, + want: "could not resolve chart values reference 'default/values': unknown error", + }, + { + name: "with wrapped error", + err: &ErrValuesReference{ + Name: types.NamespacedName{Namespace: "default", Name: "values"}, + Err: errors.New("boom"), + }, + want: "could not resolve chart values reference 'default/values': boom", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + g.Expect(func() { _ = tt.err.Error() }).ToNot(Panic()) + g.Expect(tt.err.Error()).To(Equal(tt.want)) + }) + } +} + func mockSecret(name string, data map[string][]byte) *corev1.Secret { return &corev1.Secret{ TypeMeta: metav1.TypeMeta{