ipfs/kubo · error
%s key is not a map
Error message
%s key is not a map
What it means
MapGetKV walks a dotted config key (e.g. "Addresses.API") through nested map[string]any values. At each segment it type-asserts the current level to map[string]any; if the value reached so far (path prefix "sofar") is not a map, it returns this error naming the non-map prefix. It surfaces from GetConfigKey/SetConfigKey when an intermediate config value is a scalar, not an object.
Source
Thrown at repo/common/common.go:20
import (
"fmt"
"maps"
"strings"
)
func MapGetKV(v map[string]any, key string) (any, error) {
var ok bool
var mcursor map[string]any
var cursor any = v
parts := strings.Split(key, ".")
for i, part := range parts {
sofar := strings.Join(parts[:i], ".")
mcursor, ok = cursor.(map[string]any)
if !ok {
return nil, fmt.Errorf("%s key is not a map", sofar)
}
cursor, ok = mcursor[part]
if !ok {
// Construct the current path traversed to print a nice error message
var path string
if len(sofar) > 0 {
path += sofar + "."
}
path += part
return nil, fmt.Errorf("%s not found", path)
}
}
return cursor, nil
}
func MapSetKV(v map[string]any, key string, value any) error {
var ok boolView on GitHub (pinned to 329838acdf)
Solutions
- Print the intermediate value (ipfs config <sofar>) and confirm whether it is a scalar — address it directly instead of indexing into it
- Use the correct full key for scalars: e.g. get "Addresses.API" itself, not "Addresses.API.child"
- Validate the config schema before traversal (check each prefix is map[string]any) and give the user a targeted message
- Migrate/rewrite config entries whose shape changed across versions
Example fix
// before v, err := MapGetKV(cfg, "Addresses.API.Gateway") // "Addresses key is not a map"-style failure // after v, err := MapGetKV(cfg, "Addresses.API") // API is a string scalar; read it directly
Defensive patterns
Strategy: validation
Validate before calling
func isMapPath(v map[string]any, dotted string) bool {
cur := any(v)
for _, part := range strings.Split(dotted, ".") {
m, ok := cur.(map[string]any)
if !ok {
return false
}
cur, ok = m[part]
if !ok {
return true // absent is fine; not-a-map is not
}
}
return true
} Type guard
func asConfigMap(v any) (map[string]any, bool) {
m, ok := v.(map[string]any)
return m, ok
} Try / catch
v, err := GetConfigKey(cfg, "Addresses.API.Listener")
if err != nil {
if strings.HasSuffix(err.Error(), "key is not a map") {
// fall back to reading the scalar parent
return GetConfigKey(cfg, "Addresses.API")
}
return err
} Prevention
- Check docs/config.md for which sections are maps vs scalars before using dotted keys
- Run ipfs config <prefix> to inspect intermediate values before indexing deeper
- Guard scalar parents (Addresses.API, Addresses.Gateway) against child-key access in scripts
- Validate config shape after version upgrades that change section types
When it happens
Trigger: Calling GetConfigKey(map, "Addresses.API.Listener") when config["Addresses"]["API"] is a string (like "/ip4/.../tcp/5001"), so descending into "Listener" hits a non-map. Same for any dotted key where a mid-path value is a string, number, bool, or nil.
Common situations: ipfs config <scalarParent>.<child> CLI/RPC calls; scripts assuming nested structure where the config actually stores a scalar (Addresses.API is a string, Datastore.Spec is a map — mixing them up); stale config written by an older version with a different shape.
Related errors
- %s not found
- private networking (swarm.key / LIBP2P_FORCE_PNET) does not
- Routing.AcceleratedDHTClient option is set even tho Routing.
- serveHTTPApi: GetConfig() failed: %s
- serveHTTPApi: invalid API address: %q (err: %s)
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/25afe621e6c1e408.
Report an issue: GitHub.