docker/cli · error

cannot merge volumes

Error message

cannot merge volumes: %w

What it means

Thrown by merge() when mergo.Map fails merging the `Volumes` maps of two compose Configs. mergo.Map merges map[string]VolumeConfig by key; failure is rare and usually indicates a nil destination map or a non-mergeable value type introduced by an earlier transform bug.

Solutions

  1. Check the wrapped `%w` cause from mergo for the specific key/field.
  2. Ensure both files define volumes with the same structural shape (driver/driver_opts/labels consistency).
  3. Avoid overriding an external volume with a managed one across files.

Example fix

// before
# base.yml
volumes:
  data:
    driver: local
# override.yml
volumes:
  data:
    external: true
// after
# pick one source mode per volume across all merged files
volumes:
  data:
    driver: local
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure volumes with the same key are structurally compatible before merge.
func validateMergeableVolumes(base, override map[string]types.VolumeConfig) error {
    for k, ov := range override {
        bv, ok := base[k]
        if !ok {
            continue
        }
        if bv.External.External != ov.External.External {
            return fmt.Errorf("volume %s: external flag differs across files", k)
        }
    }
    return nil
}

Try / catch

// Surface the wrapped mergo cause clearly.
if err := cfg.Load(details); err != nil {
    var me interface{ Unwrap() error }
    if errors.As(err, &me) {
        return fmt.Errorf("volume merge failed: %w", me.Unwrap())
    }
    return err
}

Prevention

When it happens

Trigger: Merging two compose files where the Volumes map merge raises an error from mergo (e.g. nil map, type mismatch after a custom transform).

Common situations: Combining `-f` files where one defines volumes and the other overrides with incompatible shapes; programmatic Config construction leaving Volumes nil.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/5384009ac9428855. Report an issue: GitHub.

Appendix: source

Thrown at cli/compose/loader/merge.go:39

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)
		}
	}
	return base, nil
}

func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {

View on GitHub (pinned to 4f84911bfe)