From c712d2d7d7c4af7c89455c9c1dd944034846ddfe Mon Sep 17 00:00:00 2001 From: chuenchen309 <48723787+chuenchen309@users.noreply.github.com> Date: Sun, 19 Jul 2026 23:03:46 +0800 Subject: [PATCH] Return an error instead of panicking on a map with an unmappable key/value type `mapDecoder` looked up the key and value mappers with `Registry.ForNamedType`, which is documented to return nil when no mapper can be determined, but used the results without a nil check. A map flag whose key or value type has no registered mapper (e.g. `map[string]any`, a very common idiom for arbitrary config, or `map[complex128]string`) therefore nil-panicked at decode time. The sibling `sliceDecoder` already guards this exact case and returns "no mapper for element type of ...". Mirror that for maps so both paths fail the same way with a descriptive error. --- kong_test.go | 16 ++++++++++++++++ mapper.go | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/kong_test.go b/kong_test.go index 34e89c9..b6abfd7 100644 --- a/kong_test.go +++ b/kong_test.go @@ -718,6 +718,22 @@ func TestMapFlagWithSliceValue(t *testing.T) { assert.Equal(t, map[string][]int{"a": {1, 2}, "b": {3}}, cli.Set) } +func TestMapFlagWithUnmappableValueType(t *testing.T) { + var cli struct { + Set map[string]any + } + _, err := mustNew(t, &cli).Parse([]string{"--set", "a=b"}) + assert.EqualError(t, err, "--set: no mapper for value type of map[string]interface {}") +} + +func TestMapFlagWithUnmappableKeyType(t *testing.T) { + var cli struct { + Set map[complex128]string + } + _, err := mustNew(t, &cli).Parse([]string{"--set", "a=b"}) + assert.EqualError(t, err, "--set: no mapper for key type of map[complex128]string") +} + type embeddedFlags struct { Embedded string } diff --git a/mapper.go b/mapper.go index 390b627..e973a5b 100644 --- a/mapper.go +++ b/mapper.go @@ -515,6 +515,9 @@ func mapDecoder(r *Registry) MapperFunc { keyScanner := ScanAsType(FlagValueToken, key) keyDecoder := r.ForNamedType(keyTypeName, el.Key()) + if keyDecoder == nil { + return fmt.Errorf("no mapper for key type of %s", target.Type()) + } keyValue := reflect.New(el.Key()).Elem() if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil { return fmt.Errorf("invalid map key %q", key) @@ -522,6 +525,9 @@ func mapDecoder(r *Registry) MapperFunc { valueScanner := ScanAsType(FlagValueToken, value) valueDecoder := r.ForNamedType(valueTypeName, el.Elem()) + if valueDecoder == nil { + return fmt.Errorf("no mapper for value type of %s", target.Type()) + } valueValue := reflect.New(el.Elem()).Elem() if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil { return fmt.Errorf("invalid map value %q", value)