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 all three of Config/File/Registry are set (service.go:701-702). The message joins the first two fields and the last into a single 'cannot specify both X, and Y' string. As with error 450, only one source may be provided.
Solutions
- Reduce credential_spec to a single one of config/file/registry.
- Delete the extra two keys and redeploy.
Example fix
// before credential_spec: config: cred-conf file: ./gmsa.json registry: myreg/gmsa // after credential_spec: config: cred-conf
Defensive patterns
Strategy: validation
Validate before calling
// Same check as 450 covers the >2 case (count keys set; reject if > 1).
func validateCredentialSpecKeys(cfg *composetypes.Config) error {
for _, svc := range cfg.Services {
cs := svc.CredentialSpec
n := 0
for _, v := range []string{cs.Config, cs.File, cs.Registry} {
if v != "" { n++ }
}
if n > 1 {
return fmt.Errorf("invalid credential spec: at most one of Config/File/Registry allowed (got %d)", n)
}
}
return nil
} Prevention
- Keep credential_spec to a single source key.
- Avoid merging multiple compose files that each set a different credential source.
- Lint credential_spec key counts in CI.
When it happens
Trigger: A service's credential_spec sets config, file, AND registry simultaneously. The slice `o` has length 3 (>2) and the switch at service.go:701 fires.
Common situations: Iterative edits that never removed prior credential-spec keys; merging compose files that each contributed a different source.
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/510c9951c882efad.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/convert/service.go:702
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, nil
}
}View on GitHub (pinned to 4f84911bfe)