docker/cli · error
invalid credential spec: cannot specify both
Error message
invalid credential spec: cannot specify both %s and %s
What it means
Returned by convertCredentialSpec when exactly two of Config/File/Registry are set on a CredentialSpec (service.go:695-700). A credential spec must source its content from exactly one place; specifying two is ambiguous and rejected.
Solutions
- Keep only one of credential_spec.config, credential_spec.file, or credential_spec.registry.
- Remove the duplicate key from the service definition and redeploy.
Example fix
// before credential_spec: file: ./gmsa-cred.json registry: myreg.example.com/gmsa // after credential_spec: registry: myreg.example.com/gmsa
Defensive patterns
Strategy: validation
Validate before calling
// A credential spec must set at most one of Config/File/Registry.
func validateCredentialSpec(cfg *composetypes.Config) error {
for _, svc := range cfg.Services {
cs := svc.CredentialSpec
set := 0
if cs.Config != "" { set++ }
if cs.File != "" { set++ }
if cs.Registry != "" { set++ }
if set > 1 {
return fmt.Errorf("invalid credential spec: set only one of Config/File/Registry")
}
}
return nil
} Prevention
- Set exactly one credential_spec source key per service.
- Remove stale keys when switching between file/config/registry sources.
- Review credential_spec blocks during compose-file reviews.
When it happens
Trigger: A service's credential_spec sets two of `config`, `file`, `registry`. The slice `o` ends with length 2 and the switch at service.go:699 fires.
Common situations: Migrating from a file-based spec to a config-based spec and leaving both keys; copy-paste adding registry while file is still set.
Related errors
- invalid credential spec: cannot specify both
- invalid credential spec: spec specifies config
- service
- undefined network
- undefined secret
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/6b0e3f4102cbcb1a.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/convert/service.go:700
func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) {
var o []string
if spec.Config != "" {
o = append(o, `"Config"`)
}
if spec.File != "" {
o = append(o, `"File"`)
}
if spec.Registry != "" {
o = append(o, `"Registry"`)
}
l := len(o)
switch {
case l == 0:
return nil, nil
case l == 2:
return nil, fmt.Errorf("invalid credential spec: cannot specify both %s and %s", o[0], o[1])
case l > 2:
return nil, fmt.Errorf("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1])
}
swarmCredSpec := swarm.CredentialSpec(spec)
// if we're using a swarm Config for the credential spec, over-write it
// here with the config ID
if swarmCredSpec.Config != "" {
for _, config := range refs {
if swarmCredSpec.Config == config.ConfigName {
swarmCredSpec.Config = config.ConfigID
return &swarmCredSpec, nil
}
}
// if none of the configs match, try namespacing
for _, config := range refs {
if namespace.Scope(swarmCredSpec.Config) == config.ConfigName {
swarmCredSpec.Config = config.ConfigID
return &swarmCredSpec, nilView on GitHub (pinned to 4f84911bfe)