{"record":{"id":"585604bbd5ef1abf","repo":"docker/cli","slug":"invalid-field-s-must-be-a-key-value-pair-585604","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/secret.go","lineNumber":46,"sourceCode":"\t\tFile: &swarm.SecretReferenceFileTarget{\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 --secret foo\n\tif len(fields) == 1 && !strings.Contains(fields[0], \"=\") {\n\t\toptions.File.Name = fields[0]\n\t\toptions.SecretName = 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\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.SecretName = 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\n\t\t\toptions.File.Mode = os.FileMode(m)","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/opts/swarmopts/secret.go#L28-L64","documentation":"Thrown by SecretOpt.Set (secret.go:46) when a field in the long-syntax --secret value is not a key=value pair or has an empty key. Mirrors the config logic: long syntax expects source=NAME,target=/path,uid=...,gid=...,mode=0440; a bare token or missing '=' is rejected. The simple `--secret NAME` shorthand only applies for a single field with no '='.","triggerScenarios":"Passing `--secret source=db_pw,target` (target lacks value), `--secret foo,bar` (bar has no '='), `--secret =x` (empty key). strings.Cut at line 44 returns ok=false or empty key.","commonSituations":"Mixing positional and key=value syntax, forgetting '=' for target/mode, copy-paste truncation, or building the --secret string with a missing value.","solutions":["Write every field as key=value: `--secret source=db_pw,target=/run/secrets/db,mode=0400`.","For the simple case use shorthand: `--secret db_pw`.","Ensure each field has a non-empty key before '='.","If a value is optional, still provide it explicitly (e.g. target=/default/path)."],"exampleFix":"// before\n--secret source=db_pw,target\n// after\n--secret source=db_pw,target=/run/secrets/db_pw","handlingStrategy":"validation","validationCode":"// Verify every CSV field of a --secret value is key=value with a non-empty key.\nfor _, f := range strings.Split(secretVal, \",\") {\n    k, _, ok := strings.Cut(strings.TrimSpace(f), \"=\")\n    if !ok || k == \"\" {\n        return fmt.Errorf(\"secret field %q must be key=value\", f)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the simple `--secret NAME` shorthand when you only need a name.","Always pair every key with a value via '='.","Avoid trailing commas that produce empty fields.","Build --secret strings field-by-field, not by comma-joining partials."],"tags":["docker","swarm","secret","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"}