docker/cli · error
cannot merge configs
Error message
cannot merge configs: %w
What it means
Raised in the Compose merge loop when dario.cat/mergo fails to deep-merge the top-level `configs` map of an override Compose file into the base. mergo.Map is called with WithOverride at merge.go:47; any returned error is wrapped with this message. It signals the `configs:` (Compose Config objects, not service configs) sections have incompatible value shapes for the same key.
Solutions
- Make every `configs:` entry with the same name use the same syntax form across all merged Compose files (prefer the long-form object in all).
- Temporarily drop the override file to confirm it is the source, then re-add once the entry is normalized.
- Validate each file individually with `docker compose config` before merging.
Example fix
// before (docker-compose.yml)
configs:
httpd: "./httpd.conf"
// before (docker-compose.override.yml)
configs:
httpd:
file: "./httpd.prod.conf"
// after (both long form)
configs:
httpd:
file: "./httpd.conf" Defensive patterns
Strategy: validation
Validate before calling
// Before merging, ensure matching config-object names share the same value kind.
for name, va := range base.Configs {
if vb, ok := override.Configs[name]; ok {
if reflect.TypeOf(va) != reflect.TypeOf(vb) {
return fmt.Errorf("config %q type mismatch", name)
}
}
} Try / catch
if _, err := loader.merge(configs); err != nil {
log.Printf("merge configs failed: %v", err)
} Prevention
- Use long-form object syntax for all top-level configs in every file.
- Validate each file independently with `docker compose config`.
- Document the expected config schema for your team to avoid ad-hoc shapes.
When it happens
Trigger: Calling `merge(configs)` with multiple Compose configs whose `Configs` maps hold conflicting types for an identical config-object name (string short-form in one, object long-form in the other).
Common situations: A project defines a top-level `configs:` entry as a short string in docker-compose.yml and as a detailed object (`external`, `file`, `name`, `template_driver`) in docker-compose.override.yml (or vice-versa). Mixing Compose file schema versions where the same config name has different representations also triggers it.
Related errors
- cannot merge secrets
- cannot merge service
- undefined config
- invalid credential spec: spec specifies config
- version mismatched between two composefiles
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/ba2865d3557598a3.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/loader/merge.go:48
base := configs[0]
for _, override := range configs[1:] {
var errs []error
if services, err := mergeServices(base.Services, override.Services); err != nil {
errs = append(errs, fmt.Errorf("cannot merge services: %w", err))
} else {
base.Services = services
}
if err := mergo.Map(&base.Volumes, &override.Volumes, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge volumes: %w", err))
}
if err := mergo.Map(&base.Networks, &override.Networks, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge networks: %w", err))
}
if err := mergo.Map(&base.Secrets, &override.Secrets, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge secrets: %w", err))
}
if err := mergo.Map(&base.Configs, &override.Configs, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge configs: %w", err))
}
if err := errors.Join(errs...); err != nil {
return nil, errors.Join(fmt.Errorf("failed to merge file %s", override.Filename), err)
}
}
return base, nil
}
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
mergeOpts := []func(*mergo.Config){
mergo.WithAppendSlice,
mergo.WithOverride,
mergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
reflect.TypeFor[[]types.ServiceSecretConfig](): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
reflect.TypeFor[[]types.ServiceConfigObjConfig](): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
reflect.PointerTo(reflect.TypeFor[types.UlimitsConfig]()): mergeUlimitsConfig,View on GitHub (pinned to 4f84911bfe)