{"record":{"id":"6b0e3f4102cbcb1a","repo":"docker/cli","slug":"invalid-credential-spec-cannot-specify-both-s-an","errorCode":null,"errorMessage":"invalid credential spec: cannot specify both %s and %s","messagePattern":"invalid credential spec: cannot specify both (.+?) and (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/convert/service.go","lineNumber":700,"sourceCode":"\nfunc convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) {\n\tvar o []string\n\n\tif spec.Config != \"\" {\n\t\to = append(o, `\"Config\"`)\n\t}\n\tif spec.File != \"\" {\n\t\to = append(o, `\"File\"`)\n\t}\n\tif spec.Registry != \"\" {\n\t\to = append(o, `\"Registry\"`)\n\t}\n\tl := len(o)\n\tswitch {\n\tcase l == 0:\n\t\treturn nil, nil\n\tcase l == 2:\n\t\treturn nil, fmt.Errorf(\"invalid credential spec: cannot specify both %s and %s\", o[0], o[1])\n\tcase l > 2:\n\t\treturn nil, fmt.Errorf(\"invalid credential spec: cannot specify both %s, and %s\", strings.Join(o[:l-1], \", \"), o[l-1])\n\t}\n\tswarmCredSpec := swarm.CredentialSpec(spec)\n\t// if we're using a swarm Config for the credential spec, over-write it\n\t// here with the config ID\n\tif swarmCredSpec.Config != \"\" {\n\t\tfor _, config := range refs {\n\t\t\tif swarmCredSpec.Config == config.ConfigName {\n\t\t\t\tswarmCredSpec.Config = config.ConfigID\n\t\t\t\treturn &swarmCredSpec, nil\n\t\t\t}\n\t\t}\n\t\t// if none of the configs match, try namespacing\n\t\tfor _, config := range refs {\n\t\t\tif namespace.Scope(swarmCredSpec.Config) == config.ConfigName {\n\t\t\t\tswarmCredSpec.Config = config.ConfigID\n\t\t\t\treturn &swarmCredSpec, nil","sourceCodeStart":682,"sourceCodeEnd":718,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/convert/service.go#L682-L718","documentation":"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.","triggerScenarios":"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.","commonSituations":"Migrating from a file-based spec to a config-based spec and leaving both keys; copy-paste adding registry while file is still set.","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."],"exampleFix":"// before\ncredential_spec:\n  file: ./gmsa-cred.json\n  registry: myreg.example.com/gmsa\n// after\ncredential_spec:\n  registry: myreg.example.com/gmsa","handlingStrategy":"validation","validationCode":"// A credential spec must set at most one of Config/File/Registry.\nfunc validateCredentialSpec(cfg *composetypes.Config) error {\n    for _, svc := range cfg.Services {\n        cs := svc.CredentialSpec\n        set := 0\n        if cs.Config != \"\" { set++ }\n        if cs.File != \"\" { set++ }\n        if cs.Registry != \"\" { set++ }\n        if set > 1 {\n            return fmt.Errorf(\"invalid credential spec: set only one of Config/File/Registry\")\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["compose","convert","credential-spec","swarm","windows","gmsa"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}