docker/cli · error
%[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict…
Error message
%[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict; only use %[1]s.name
What it means
Returned by loadFileObjectConfig for secrets and configs when `external: true` is set and both `external.name` (deprecated) and `name` are present. objType is interpolated as 'secret' or 'config'.
Solutions
- Remove the deprecated `external.name` and keep `name`.
- Ensure only one of the two fields is set across all merged compose files.
Example fix
# before
secrets:
token:
external:
name: prod-token
name: prod-token
# after
secrets:
token:
external: true
name: prod-token Defensive patterns
Strategy: validation
Validate before calling
func validateFileObjectName(objType, name string, obj map[string]any) error {
ext, _ := obj["external"]
var extName string
if em, ok := ext.(map[string]any); ok {
extName, _ = em["name"].(string)
}
topName, _ := obj["name"].(string)
if ext == true && extName != "" && topName != "" {
return fmt.Errorf("%s %s: set only one of external.name or name", objType, name)
}
return nil
} Prevention
- Use `name` for secrets/configs; drop `external.name`.
- Keep external-name logic in a single file when merging.
- Validate rendered output with `docker compose config`.
When it happens
Trigger: A `secrets:` or `configs:` entry has `external: true` plus `external.name` and `name` both non-empty.
Common situations: Porting an old compose file using `external.name` to the modern `name` field and leaving both; CI override file sets `name` while base sets `external.name`.
Related errors
- network : network.external.name and network.name conflict…
- volume : volume.external.name and volume.name conflict…
- %[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use…
- conflicting parameters "external" and
- invalid type %T for secret
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/fcb1dd34dccf960c.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/loader/loader.go:656
obj, err := loadFileObjectConfig(name, "config", types.FileObjectConfig(config), details)
if err != nil {
return nil, err
}
configConfig := types.ConfigObjConfig(obj)
configConfig.Extras = loadExtras(name, source)
configs[name] = configConfig
}
return configs, nil
}
func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfig, details types.ConfigDetails) (types.FileObjectConfig, error) {
// if "external: true"
switch {
case obj.External.External:
// handle deprecated external.name
if obj.External.Name != "" {
if obj.Name != "" {
return obj, fmt.Errorf("%[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict; only use %[1]s.name", objType, name)
}
if versions.GreaterThanOrEqualTo(details.Version, "3.5") {
logrus.Warnf("%[1]s %[2]s: %[1]s.external.name is deprecated in favor of %[1]s.name", objType, name)
}
obj.Name = obj.External.Name
obj.External.Name = ""
} else if obj.Name == "" {
obj.Name = name
}
// if not "external: true"
case obj.Driver != "":
if obj.File != "" {
return obj, fmt.Errorf("%[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use %[1]s.driver", objType, name)
}
default:
obj.File = absPath(details.WorkingDir, obj.File)
}
View on GitHub (pinned to 4f84911bfe)