docker/cli · error
cannot merge services
Error message
cannot merge services: %w
What it means
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.
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.
Example fix
# before
# base: web.ulimits.nproc: 65535
# override: web.ulimits.nproc: { soft: 1024 } # shape mismatch
# after
# base: web.ulimits.nproc: { soft: 65535, hard: 65535 }
# override: web.ulimits.nproc: { soft: 1024, hard: 1024 } Defensive patterns
Strategy: try-catch
Validate before calling
// Before merging, ensure services with the same name share field shapes.
func validateMergeableServices(base, override []types.ServiceConfig) error {
idx := map[string]types.ServiceConfig{}
for _, s := range base {
idx[s.Name] = s
}
for _, o := range override {
b, ok := idx[o.Name]
if !ok {
continue
}
// example shape check: ulimits must both be map or both empty
if err := compatibleUlimits(b.Ulimits, o.Ulimits); err != nil {
return fmt.Errorf("service %s: %w", o.Name, err)
}
}
return nil
} Try / catch
// merge() returns errors; wrap to add file context.
func loadMultiple(details types.ConfigDetails) (*types.Config, error) {
cfg, err := Load(details)
if err != nil {
// errors.Is/As to detect "cannot merge services"
var mergeErr *mergeErrMatcher
if errors.As(err, &mergeErr) {
return nil, fmt.Errorf("override merge failed (align service shapes across -f files): %w", err)
}
return nil, err
}
return cfg, nil
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- cannot merge volumes
- cannot merge networks
- service
- version mismatched between two composefiles
- invalid type %T for ulimits
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/4e0a21d2a86167bc.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/loader/merge.go:34
)
type specials struct {
m map[reflect.Type]func(dst, src reflect.Value) error
}
func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
if fn, ok := s.m[t]; ok {
return fn
}
return nil
}
func merge(configs []*types.Config) (*types.Config, error) {
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)
}View on GitHub (pinned to 4f84911bfe)