From d99343a62aa8cde5848b1f05c5abb68c860c514f Mon Sep 17 00:00:00 2001 From: skyfire Date: Mon, 12 Oct 2026 00:26:23 +0800 Subject: [PATCH] fix: skip empty header values in bindData to avoid panic BindHeaders panics with "index out of range [0] with length 0" when a header value is a nil or empty []string. Skip entries with no values in both the map and struct binding paths. Fixes #3169 Co-Authored-By: Jarvis-AI-42 --- bind.go | 8 ++++++++ bind_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/bind.go b/bind.go index bec2e3b3a..4dfd6d3c8 100644 --- a/bind.go +++ b/bind.go @@ -241,6 +241,10 @@ func bindData(destination any, data map[string][]string, tag string, dataFiles m val.Set(reflect.MakeMap(typ)) } for k, v := range data { + if len(v) == 0 { + // skip entries with no values to avoid panicking on v[0] + continue + } if isElemString { val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) } else if isElemInterface { @@ -322,6 +326,10 @@ func bindData(destination any, data map[string][]string, tag string, dataFiles m if !exists { continue } + if len(inputValue) == 0 { + // skip entries with no values to avoid panicking on inputValue[0] + continue + } // NOTE: algorithm here is not particularly sophisticated. It probably does not work with absurd types like `**[]*int` // but it is smart enough to handle niche cases like `*int`,`*[]string`,`[]*int` . diff --git a/bind_test.go b/bind_test.go index 771c59fb8..743a9f44a 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1873,3 +1873,33 @@ func TestOpenAPIFormatTimeBindingEdgeCases(t *testing.T) { }) } } + +func TestBindHeadersEmptyValues(t *testing.T) { + // Regression test for https://github.com/labstack/echo/issues/3169 + // BindHeaders must not panic when a header value is a nil or empty []string. + for _, values := range [][]string{nil, {}} { + t.Run(fmt.Sprintf("values=%v", values), func(t *testing.T) { + e := New() + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header["X-Empty"] = values + c := e.NewContext(req, httptest.NewRecorder()) + + var dst struct { + Value string `header:"X-Empty"` + } + if err := BindHeaders(c, &dst); err != nil { + t.Errorf("BindHeaders: %v", err) + } + + var m map[string]string + if err := BindHeaders(c, &m); err != nil { + t.Errorf("BindHeaders(map[string]string): %v", err) + } + + var anyMap map[string]any + if err := BindHeaders(c, &anyMap); err != nil { + t.Errorf("BindHeaders(map[string]any): %v", err) + } + }) + } +}