go-kratos/kratos · error

config: merge dst must be *map[string]interface{}, got %T

Error message

config: merge dst must be *map[string]interface{}, got %T

What it means

config.defaultMerge requires the merge destination to be *map[string]interface{} so it can allocate a nil destination map in place; any other dynamic type fails this check. In practice it appears when a custom Merger (registered via the config merger option) forwards a wrongly-typed dst to the default merger, or internal merge APIs are called directly.

Source

Thrown at config/merge.go:8

package config

import "fmt"

func defaultMerge(dst, src any) error {
	dstMap, ok := dst.(*map[string]any)
	if !ok {
		return fmt.Errorf("config: merge dst must be *map[string]interface{}, got %T", dst)
	}
	srcMap, ok := convertMap(src).(map[string]any)
	if !ok {
		return fmt.Errorf("config: merge src must be map[string]interface{}, got %T", src)
	}
	if *dstMap == nil {
		*dstMap = make(map[string]any, len(srcMap))
	}
	mergeMap(*dstMap, srcMap)
	return nil
}

func mergeMap(dst, src map[string]any) {
	for key, srcValue := range src {
		if srcMap, ok := srcValue.(map[string]any); ok {
			if dstMap, ok := dst[key].(map[string]any); ok {
				mergeMap(dstMap, srcMap)
				continue

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Pass the destination as *map[string]any (pointer), never a map value
  2. If you need different semantics, register a full custom Merger instead of partially reusing defaultMerge
  3. Convert unknown-shaped values with the package's convertMap before merging

Example fix

// before
var dst map[string]any
_ = defaultMerge(dst, src) // passed by value: merge dst must be *map[string]interface{}

// after
var dst map[string]any
_ = defaultMerge(&dst, src)
Defensive patterns

Strategy: type-guard

Type guard

func isMergeableDst(dst any) bool {
    _, ok := dst.(*map[string]any)
    return ok
}

Try / catch

if err := defaultMerge(dst, src); err != nil {
    if strings.HasPrefix(err.Error(), "config: merge dst") {
        dstMap := map[string]any{}
        err = defaultMerge(&dstMap, src) // correct shape, retry once
    }
    if err != nil {
        return err
    }
}

Prevention

When it happens

Trigger: defaultMerge invoked with dst of any type other than *map[string]any — e.g. a custom Merger passing a map by value, or an unrelated type, instead of a pointer to the map.

Common situations: Writing a custom merger to customize deep-merge behavior and delegating to the default with a reshaped dst; code copied from tests that call merge internals directly.

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/02b756ef38556508. Report an issue: GitHub.