{"record":{"id":"6cf14ac4e8ade265","repo":"docker/cli","slug":"invalid-field-s-must-be-a-key-value-pair-6cf14a","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":"opts/swarmopts/config.go","lineNumber":46,"sourceCode":"\t\tFile: &swarm.ConfigReferenceFileTarget{\n\t\t\tUID:  \"0\",\n\t\t\tGID:  \"0\",\n\t\t\tMode: 0o444,\n\t\t},\n\t}\n\n\t// support a simple syntax of --config foo\n\tif len(fields) == 1 && !strings.Contains(fields[0], \"=\") {\n\t\toptions.File.Name = fields[0]\n\t\toptions.ConfigName = fields[0]\n\t\to.values = append(o.values, options)\n\t\treturn nil\n\t}\n\n\tfor _, field := range fields {\n\t\tkey, val, ok := strings.Cut(field, \"=\")\n\t\tif !ok || key == \"\" {\n\t\t\treturn 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 \"source\", \"src\":\n\t\t\toptions.ConfigName = val\n\t\tcase \"target\":\n\t\t\toptions.File.Name = val\n\t\tcase \"uid\":\n\t\t\toptions.File.UID = val\n\t\tcase \"gid\":\n\t\t\toptions.File.GID = val\n\t\tcase \"mode\":\n\t\t\tm, err := strconv.ParseUint(val, 0, 32)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"invalid mode specified: %v\", err)\n\t\t\t}\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/opts/swarmopts/config.go#L28-L64","documentation":"Thrown by ConfigOpt.Set (config.go:46) when a field in the CSV-parsed --config value is not a key=value pair or has an empty key. In long syntax, `docker service create --config` expects fields like source=NAME,target=/path,uid=...,mode=0440; a bare token or one missing '=' is rejected. The simple `--config NAME` shorthand only applies when there is a single field with no '='.","triggerScenarios":"Passing `--config source=myconf,target` (target has no value/equals), `--config foo,bar` where bar lacks '=', `--config =x` (empty key), or a CSV field containing only whitespace. strings.Cut at line 44 returns ok=false or key=\"\".","commonSituations":"Mixing positional and key=value syntax, forgetting the '=' for the target field, copy-paste truncation, or building the --config string via comma-join with a missing value.","solutions":["Write every field as key=value: `--config source=myconf,target=/etc/app/conf,mode=0440`.","For the simple case use the shorthand: `--config myconf` (single token, no '=').","Make sure no field is empty and each has a non-empty key before the '='.","If a value is optional, still write `target=/default/path` explicitly."],"exampleFix":"// before\n--config source=myconf,target\n// after\n--config source=myconf,target=/etc/app/conf","handlingStrategy":"validation","validationCode":"// Verify every CSV field of a --config value is key=value with a non-empty key.\nfor _, f := range strings.Split(cfgVal, \",\") {\n    k, _, ok := strings.Cut(strings.TrimSpace(f), \"=\")\n    if !ok || k == \"\" {\n        return fmt.Errorf(\"config field %q must be key=value\", f)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the simple `--config NAME` shorthand when you only need a name.","Always pair every key with a value via '='.","Avoid trailing commas that produce empty fields.","Construct --config strings field-by-field, not by comma-joining partials."],"tags":["docker","swarm","config","validation","cli","secrets-configs"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}