docker/cli · error

volume : volume.external.name and volume.name conflict…

Error message

volume %s: volume.external.name and volume.name conflict; only use volume.name

What it means

Thrown by LoadVolumes for an external volume that sets BOTH `external.name` (deprecated) and the top-level `name`. Like the network case, the two rename the external resource and are mutually exclusive.

Solutions

  1. Keep `name` and remove `external.name`.
  2. Or keep only the deprecated `external.name` and remove `name` (not recommended).

Example fix

# before
volumes:
  data:
    external:
      name: prod-data
    name: prod-data
# after
volumes:
  data:
    external: true
    name: prod-data
Defensive patterns

Strategy: validation

Validate before calling

func validateVolumeName(name string, vol map[string]any) error {
    ext, _ := vol["external"]
    var extName string
    if em, ok := ext.(map[string]any); ok {
        extName, _ = em["name"].(string)
    }
    topName, _ := vol["name"].(string)
    if ext == true && extName != "" && topName != "" {
        return fmt.Errorf("volume %s: set only one of external.name or name", name)
    }
    return nil
}

Prevention

When it happens

Trigger: A volume entry has `external: true` (or `external: {name: x}`) and both `external.name` and `name` are non-empty.

Common situations: Migrating off the deprecated `external.name` field; merge of two override files each setting a different field.

Related errors


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

Appendix: source

Thrown at cli/compose/loader/loader.go:595

	volumes := make(map[string]types.VolumeConfig)
	if err := Transform(source, &volumes); err != nil {
		return volumes, err
	}

	for name, volume := range volumes {
		if !volume.External.External {
			continue
		}
		switch {
		case volume.Driver != "":
			return nil, externalVolumeError(name, "driver")
		case len(volume.DriverOpts) > 0:
			return nil, externalVolumeError(name, "driver_opts")
		case len(volume.Labels) > 0:
			return nil, externalVolumeError(name, "labels")
		case volume.External.Name != "":
			if volume.Name != "" {
				return nil, fmt.Errorf("volume %s: volume.external.name and volume.name conflict; only use volume.name", name)
			}
			if versions.GreaterThanOrEqualTo(version, "3.4") {
				logrus.Warnf("volume %s: volume.external.name is deprecated in favor of volume.name", name)
			}
			volume.Name = volume.External.Name
			volume.External.Name = ""
		case volume.Name == "":
			volume.Name = name
		}
		volume.Extras = loadExtras(name, source)
		volumes[name] = volume
	}
	return volumes, nil
}

// LoadSecrets produces a SecretConfig map from a compose file Dict
// the source Dict is not validated if directly used. Use Load() to enable validation
func LoadSecrets(source map[string]any, details types.ConfigDetails) (map[string]types.SecretConfig, error) {

View on GitHub (pinned to 4f84911bfe)