# MapKeys returns an empty slice for a natively-typed map ## Problem `Koanf.MapKeys(path)` is documented to return the sorted keys of the map at `path`, or an empty slice "if the path is not a map". A value at a key path does not have to be `map[string]any` to be a map, though: `Set()`, or a provider such as `structs`, can put a `map[string]T` (`map[string]int64`, `map[string]bool`, ...) directly into the config tree, and `Koanf.Get()` returns it verbatim with its concrete type intact (`koanf.go`'s `Get` only special-cases `map[string]any` for its copy, everything else falls through to `copystructure.Copy`, which preserves the type). `MapKeys` only checked `o.(map[string]any)`, so a natively-typed map failed that assertion and produced `[]string{}` even though the map plainly has keys - a silent contract violation, not a "not a map" case. ```go k := koanf.New(".") k.Load(confmap.Provider(map[string]any{ "ints": map[string]int64{"a": 1, "b": 2}, }, "."), nil) k.Get("ints") // map[string]int64{"a":1, "b":2} k.MapKeys("ints") // []string{} -- should be []string{"a", "b"} ``` ## Root cause `koanf.go:410-429`, `MapKeys`: ```go mp, ok := o.(map[string]any) if !ok { return out } ``` This is the same shape of bug already fixed for `Int64Map`, `Float64Map` and `BoolMap` in #444 (checking only `map[string]any` and silently dropping the natively-typed sibling), applied to a different getter that PR does not touch. ## Fix Since `MapKeys` only needs the keys and does not care about the value type, the fix reads them through reflection instead of enumerating every possible `map[string]T`: any map whose key kind is `string` is accepted, `map[string]any` included. ```go rv := reflect.ValueOf(o) if rv.Kind() != reflect.Map || rv.Type().Key().Kind() != reflect.String { return out } out = make([]string, 0, rv.Len()) for _, k := range rv.MapKeys() { out = append(out, k.String()) } sort.Strings(out) ``` `reflect` and `sort` are already imported in `koanf.go` for other methods. ## Verification `TestMapKeysNativeMap` in `tests/koanf_test.go` loads a `map[string]int64` and asserts `MapKeys` returns its keys. Reverting only the `MapKeys` body in `koanf.go` (test left in place) fails it: ``` --- FAIL: TestMapKeysNativeMap Error: Not equal: expected: []string{"a", "b"} actual : []string{} Messages: map keys mismatch for a natively-typed map ``` `go build ./...`, `go vet ./...` and `gofmt -l .` are clean in both the root module and `tests/` (the only `gofmt -l` hit is the pre-existing `examples/read-huml/main.go`, untouched by this change). `go test ./... -count=1` and `go test ./... -count=1 -race` in `tests/`: 60 tests, all passing, including the new one. ## Flags for the account owner - No CLA/DCO step found in `CONTRIBUTING.md` or `.github/`. - Behaviour change: code that relied on `MapKeys` returning `[]string{}` for a natively-typed map (rather than actually reading its keys) would see a different result now. I think the old behaviour was simply wrong given the doc comment, but flagging it since #444 called out the same kind of change for its three getters.