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

  1. Keep only one of credential_spec.config, credential_spec.file, or credential_spec.registry.
  2. 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

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


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, nil

View on GitHub (pinned to 4f84911bfe)