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
- Keep `name` and remove `external.name`.
- 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
- Migrate fully off `external.name` to `name`.
- Avoid setting both fields across merged override files.
- Lint with `docker compose config`.
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
- network : network.external.name and network.name conflict…
- conflicting parameters "external" and
- %[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict…
- invalid type %T for external
- %[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use…
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)