{"record":{"id":"7a3291d97b1ceb0f","repo":"docker/cli","slug":"invalid-field-s-must-be-a-key-value-pair","errorCode":null,"errorMessage":"invalid field '%s' must be a key=value pair","messagePattern":"invalid field '(.+?)' must be a key=value pair","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/swarm/opts.go","lineNumber":180,"sourceCode":"\tcsvReader := csv.NewReader(strings.NewReader(caSpec))\n\tfields, err := csvReader.Read()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\texternalCA := swarm.ExternalCA{\n\t\tOptions: make(map[string]string),\n\t}\n\n\tvar (\n\t\thasProtocol bool\n\t\thasURL      bool\n\t)\n\n\tfor _, field := range fields {\n\t\tkey, value, ok := strings.Cut(field, \"=\")\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"invalid field '%s' must be a key=value pair\", field)\n\t\t}\n\n\t\t// TODO(thaJeztah): these options should not be case-insensitive.\n\t\tswitch strings.ToLower(key) {\n\t\tcase \"protocol\":\n\t\t\thasProtocol = true\n\t\t\tif strings.ToLower(value) == string(swarm.ExternalCAProtocolCFSSL) {\n\t\t\t\texternalCA.Protocol = swarm.ExternalCAProtocolCFSSL\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized external CA protocol %s\", value)\n\t\t\t}\n\t\tcase \"url\":\n\t\t\thasURL = true\n\t\t\texternalCA.URL = value\n\t\tcase \"cacert\":\n\t\t\tcacontents, err := os.ReadFile(value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to read CA cert for external CA: %w\", err)","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/command/swarm/opts.go#L162-L198","documentation":"Raised while parsing the --external-ca CSV spec when a field has no '=' separator. parseExternalCA uses strings.Cut on '='; a token like 'protocol' (instead of 'protocol=cfssl') fails the cut and yields this error.","triggerScenarios":"Passing 'docker swarm <cmd> --external-ca protocol' (no =value), or any field in the comma-separated spec missing its '='. Also triggered by malformed CSV where a value contains an unquoted comma that splits into a bare token.","commonSituations":"Typo'd spec ('protocol' instead of 'protocol=cfssl'); copy-paste that lost the '='; embedded commas not quoted.","solutions":["Ensure every field is key=value, e.g. 'protocol=cfssl,url=https://ca.example.com'.","Quote fields containing commas using CSV quoting (double quotes).","Check required keys: protocol and url are both mandatory."],"exampleFix":"# before\ndocker swarm init --external-ca protocol,url=https://ca.example.com\n\n# after\ndocker swarm init --external-ca protocol=cfssl,url=https://ca.example.com","handlingStrategy":"validation","validationCode":"// Validate each external-ca field has '='\nfor _, f := range strings.Split(spec, \",\") {\n    if !strings.Contains(f, \"=\") {\n        return fmt.Errorf(\"invalid field %q: must be key=value\", f)\n    }\n}","typeGuard":"func isKeyValuePair(s string) bool {\n\t_, _, ok := strings.Cut(s, \"=\")\n\treturn ok\n}","tryCatchPattern":null,"preventionTips":["Always use key=value pairs in --external-ca.","Quote fields containing commas.","Remember protocol and url are both required."],"tags":["swarm","external-ca","parsing","validation"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}