{"record":{"id":"af0288ccfa9298ca","repo":"kubernetes/kops","slug":"cannot-parse-flag-spec-q","errorCode":null,"errorMessage":"cannot parse flag spec: %q","messagePattern":"cannot parse flag spec: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/flagbuilder/build_flags.go","lineNumber":79,"sourceCode":"\t\t}\n\t\tif tag == \"-\" {\n\t\t\tklog.V(4).Infof(\"skipping field with %q flag tag: %s\", tag, path)\n\t\t\treturn reflectutils.SkipReflection\n\t\t}\n\n\t\t// If we specify the repeat option, we will repeat the flag rather than joining it with commas\n\t\trepeatFlag := false\n\n\t\ttokens := strings.Split(tag, \",\")\n\t\tif len(tokens) > 1 {\n\t\t\tfor i, t := range tokens {\n\t\t\t\tif i == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif t == \"repeat\" {\n\t\t\t\t\trepeatFlag = true\n\t\t\t\t} else {\n\t\t\t\t\treturn fmt.Errorf(\"cannot parse flag spec: %q\", tag)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tflagName := tokens[0]\n\n\t\t// If the \"unset\" value is not empty string, by setting this tag we avoid passing spurious flag values\n\t\tflagEmpty := field.Tag.Get(\"flag-empty\")\n\n\t\tflagIncludeEmpty, _ := strconv.ParseBool(field.Tag.Get(\"flag-include-empty\"))\n\n\t\t// We do have to do this, even though the recursive walk will do it for us\n\t\t// because when we descend we won't have `field` set\n\t\tif val.Kind() == reflect.Ptr && reflect.TypeOf(val.Interface()).String() != \"*string\" {\n\t\t\tif val.IsNil() {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tval = val.Elem()\n\t\t}","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/flagbuilder/build_flags.go#L61-L97","documentation":"flagbuilder parses struct tags of the form `flag:\"name,option,...\"`. The only option currently recognized after the flag name is `repeat`; any other comma-separated token makes the walker abort with this error. It is a programmer error in the tag definition, not a runtime/user issue.","triggerScenarios":"A struct field has a `flag` tag containing extra tokens after the flag name that are not exactly `repeat`, e.g. `flag:\"my-flag,repeated\"` or `flag:\"my-flag,foo\"`; BuildFlags/BuildFlagsList then hits the walker at build_flags.go:79.","commonSituations":"Typos in the tag (writing 'repeats' or 'repeat-all' instead of 'repeat'), copying tag conventions from other libraries, or adding new tag options without extending the parser.","solutions":["Fix the struct tag to use only `flag:\"name\"` or `flag:\"name,repeat\"`","Remove the extra tokens from the tag if the option was not intended","If a new option is genuinely needed, extend the parser at build_flags.go:76-80 to recognize it"],"exampleFix":"// before\nMaxPods int32 `flag:\"max-pods,repeate\"`\n// after\nMaxPods int32 `flag:\"max-pods\"`","handlingStrategy":"validation","validationCode":"// Before building flags, sanity-check every flag tag in your option structs\nt := reflect.TypeOf(MyOptions{})\nfor i := 0; i < t.NumField(); i++ {\n\ttag := t.Field(i).Tag.Get(\"flag\")\n\tfor _, tok := range strings.Split(tag, \",\")[1:] {\n\t\tif tok != \"repeat\" {\n\t\t\tpanic(fmt.Sprintf(\"invalid flag tag %q on field %s\", tag, t.Field(i).Name))\n\t\t}\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only use `flag:\"name\"` or `flag:\"name,repeat\"` tag forms","Grep struct tags for flag:,... patterns before committing new option fields","Add a unit test calling BuildFlagsList on your options struct at init/CI time"],"tags":["reflection","struct-tags","flagbuilder","configuration"],"backgroundTag":"invalid-struct-tag","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}