{"record":{"id":"ba2865d3557598a3","repo":"docker/cli","slug":"cannot-merge-configs-w","errorCode":null,"errorMessage":"cannot merge configs: %w","messagePattern":"cannot merge configs: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/loader/merge.go","lineNumber":48,"sourceCode":"\tbase := configs[0]\n\tfor _, override := range configs[1:] {\n\t\tvar errs []error\n\t\tif services, err := mergeServices(base.Services, override.Services); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"cannot merge services: %w\", err))\n\t\t} else {\n\t\t\tbase.Services = services\n\t\t}\n\t\tif err := mergo.Map(&base.Volumes, &override.Volumes, mergo.WithOverride); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"cannot merge volumes: %w\", err))\n\t\t}\n\t\tif err := mergo.Map(&base.Networks, &override.Networks, mergo.WithOverride); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"cannot merge networks: %w\", err))\n\t\t}\n\t\tif err := mergo.Map(&base.Secrets, &override.Secrets, mergo.WithOverride); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"cannot merge secrets: %w\", err))\n\t\t}\n\t\tif err := mergo.Map(&base.Configs, &override.Configs, mergo.WithOverride); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"cannot merge configs: %w\", err))\n\t\t}\n\t\tif err := errors.Join(errs...); err != nil {\n\t\t\treturn nil, errors.Join(fmt.Errorf(\"failed to merge file %s\", override.Filename), err)\n\t\t}\n\t}\n\treturn base, nil\n}\n\nfunc mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {\n\tmergeOpts := []func(*mergo.Config){\n\t\tmergo.WithAppendSlice,\n\t\tmergo.WithOverride,\n\t\tmergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{\n\t\t\treflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()):        safelyMerge(mergeLoggingConfig),\n\t\t\treflect.TypeFor[[]types.ServicePortConfig]():                     mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),\n\t\t\treflect.TypeFor[[]types.ServiceSecretConfig]():                   mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),\n\t\t\treflect.TypeFor[[]types.ServiceConfigObjConfig]():                mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),\n\t\t\treflect.PointerTo(reflect.TypeFor[types.UlimitsConfig]()):        mergeUlimitsConfig,","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/loader/merge.go#L30-L66","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before (docker-compose.yml)\nconfigs:\n  httpd: \"./httpd.conf\"\n// before (docker-compose.override.yml)\nconfigs:\n  httpd:\n    file: \"./httpd.prod.conf\"\n// after (both long form)\nconfigs:\n  httpd:\n    file: \"./httpd.conf\"","handlingStrategy":"validation","validationCode":"// Before merging, ensure matching config-object names share the same value kind.\nfor name, va := range base.Configs {\n    if vb, ok := override.Configs[name]; ok {\n        if reflect.TypeOf(va) != reflect.TypeOf(vb) {\n            return fmt.Errorf(\"config %q type mismatch\", name)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := loader.merge(configs); err != nil {\n    log.Printf(\"merge configs failed: %v\", err)\n}","preventionTips":["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."],"tags":["compose","merge","configs","mergo"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}