From ab5ba27695f547017938d10655a125e1d85264ad Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Sat, 29 Aug 2026 23:49:34 +0800 Subject: [PATCH 1/2] query: skip nil encoder interface fields instead of panicking A struct field with an interface type that implements Encoder, left nil, falls through the interface unwrap (which only fires for non-nil values) and hits sv.Interface().(Encoder) on a nil interface, which panics with "interface conversion: interface is nil, not query.Encoder". Treat a nil encoder interface like any other empty value and skip the field. --- query/encode.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/query/encode.go b/query/encode.go index ffb2097..839ce5d 100644 --- a/query/encode.go +++ b/query/encode.go @@ -192,6 +192,12 @@ func reflectValue(values url.Values, val reflect.Value, scope string) error { } if sv.Type().Implements(encoderType) { + // a nil interface value has no concrete encoder to call; skip the + // field instead of panicking on the type assertion below + if sv.Kind() == reflect.Interface && sv.IsNil() { + continue + } + // if sv is a nil pointer and the custom encoder is defined on a non-pointer // method receiver, set sv to the zero value of the underlying type if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(encoderType) { From 15799b45fd5f039603b388cb543ee42148d00253 Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Sat, 29 Aug 2026 23:49:41 +0800 Subject: [PATCH 2/2] query: add regression test for nil encoder interface fields Values() on a struct with a nil interface field that implements Encoder used to panic; the test locks in the skip behavior and confirms non-nil values still go through their custom encoder. --- query/encode_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/query/encode_test.go b/query/encode_test.go index 241e4f1..9028e79 100644 --- a/query/encode_test.go +++ b/query/encode_test.go @@ -669,6 +669,39 @@ func TestValues_CustomEncodingPointer(t *testing.T) { } } +// customEncoder is an interface that embeds Encoder. +type customEncoder interface { + Encoder +} + +// TestValues_NilEncoderInterface verifies that a nil interface field whose +// type implements Encoder is skipped instead of panicking, and that a non-nil +// value is still encoded normally. +func TestValues_NilEncoderInterface(t *testing.T) { + type st struct { + V customEncoder + } + + // nil interface field: nothing to encode, must not panic + v, err := Values(st{}) + if err != nil { + t.Fatalf("Values() returned error: %v", err) + } + if len(v) != 0 { + t.Errorf("Values() = %v, want no values for a nil interface field", v) + } + + // non-nil interface field still uses the concrete encoder + v, err = Values(st{V: customEncodedStrings{"a"}}) + if err != nil { + t.Fatalf("Values() returned error: %v", err) + } + want := url.Values{"V.0": {"a"}} + if diff := cmp.Diff(want, v); diff != "" { + t.Errorf("Values() mismatch:\n%s", diff) + } +} + func TestIsEmptyValue(t *testing.T) { str := "string" tests := []struct {