{"record":{"id":"4e0a21d2a86167bc","repo":"docker/cli","slug":"cannot-merge-services-w","errorCode":null,"errorMessage":"cannot merge services: %w","messagePattern":"cannot merge services: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/loader/merge.go","lineNumber":34,"sourceCode":")\n\ntype specials struct {\n\tm map[reflect.Type]func(dst, src reflect.Value) error\n}\n\nfunc (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error {\n\tif fn, ok := s.m[t]; ok {\n\t\treturn fn\n\t}\n\treturn nil\n}\n\nfunc merge(configs []*types.Config) (*types.Config, error) {\n\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}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/loader/merge.go#L16-L52","documentation":"Thrown by merge() when merging the Services slices of two compose Configs fails. It wraps the error from mergeServices, which itself wraps per-service merge errors (e.g. `cannot merge service X: ...`). The underlying cause is typically mergo.Merge failing on incompatible struct values or a transformer error.","triggerScenarios":"Loading multiple compose files (base + override) where a service with the same name has structurally incompatible fields that mergo's transformers cannot reconcile (e.g. conflicting ulimits, nil-pointer fields).","commonSituations":"Using `-f base.yml -f override.yml` (or COMPOSE_FILE) with services that disagree on types; overriding a service that was defined differently in base.","solutions":["Inspect the wrapped `%w` cause: it names the specific service and the underlying mergo error.","Make the conflicting service fields consistent across files (same type/shape).","Simplify the override to only change scalar values, not structural fields."],"exampleFix":"# before\n# base: web.ulimits.nproc: 65535\n# override: web.ulimits.nproc: { soft: 1024 }  # shape mismatch\n# after\n# base: web.ulimits.nproc: { soft: 65535, hard: 65535 }\n# override: web.ulimits.nproc: { soft: 1024, hard: 1024 }","handlingStrategy":"try-catch","validationCode":"// Before merging, ensure services with the same name share field shapes.\nfunc validateMergeableServices(base, override []types.ServiceConfig) error {\n    idx := map[string]types.ServiceConfig{}\n    for _, s := range base {\n        idx[s.Name] = s\n    }\n    for _, o := range override {\n        b, ok := idx[o.Name]\n        if !ok {\n            continue\n        }\n        // example shape check: ulimits must both be map or both empty\n        if err := compatibleUlimits(b.Ulimits, o.Ulimits); err != nil {\n            return fmt.Errorf(\"service %s: %w\", o.Name, err)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// merge() returns errors; wrap to add file context.\nfunc loadMultiple(details types.ConfigDetails) (*types.Config, error) {\n    cfg, err := Load(details)\n    if err != nil {\n        // errors.Is/As to detect \"cannot merge services\"\n        var mergeErr *mergeErrMatcher\n        if errors.As(err, &mergeErr) {\n            return nil, fmt.Errorf(\"override merge failed (align service shapes across -f files): %w\", err)\n        }\n        return nil, err\n    }\n    return cfg, nil\n}","preventionTips":["Keep the same service shape across base and override files.","Read the wrapped `%w` cause to find the offending service.","Use `docker compose config` to preview the merged result."],"tags":["compose","merge","override","service"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}