From 219bbcd219fa61d8ec48936c6d11b0f6c27be4e1 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Fri, 4 Sep 2026 08:08:58 +0200 Subject: [PATCH] fix: MapKeys returns empty slice for natively-typed maps Get() preserves the concrete type of a map[string]T value loaded via Set() or a provider like structs, but MapKeys() only type-asserted map[string]any, so it silently reported no keys for anything else. Read keys via reflection instead so any map with string keys works. --- koanf.go | 13 ++++++++----- tests/koanf_test.go | 11 +++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/koanf.go b/koanf.go index 92ac982..b4bae13 100644 --- a/koanf.go +++ b/koanf.go @@ -413,13 +413,16 @@ func (ko *Koanf) MapKeys(path string) []string { return out } - mp, ok := o.(map[string]any) - if !ok { + // A value set with Set() or by a Provider such as structs keeps its native + // map type, so the keys have to be read through reflection too. + rv := reflect.ValueOf(o) + if rv.Kind() != reflect.Map || rv.Type().Key().Kind() != reflect.String { return out } - out = make([]string, 0, len(mp)) - for k := range mp { - out = append(out, k) + + out = make([]string, 0, rv.Len()) + for _, k := range rv.MapKeys() { + out = append(out, k.String()) } sort.Strings(out) return out diff --git a/tests/koanf_test.go b/tests/koanf_test.go index f402572..a8d772e 100644 --- a/tests/koanf_test.go +++ b/tests/koanf_test.go @@ -1691,6 +1691,17 @@ func TestTypedGettersNativeMaps(t *testing.T) { } } +func TestMapKeysNativeMap(t *testing.T) { + assert := assert.New(t) + + k := koanf.New(delim) + assert.NoError(k.Load(confmap.Provider(map[string]any{ + "ints": map[string]int64{"a": 1, "b": 2}, + }, "."), nil)) + + assert.Equal([]string{"a", "b"}, k.MapKeys("ints"), "map keys mismatch for a natively-typed map") +} + // waitTimeout waits for the waitgroup for the specified max timeout. // Returns true if waiting timed out. func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {