{"record":{"id":"7fc3a5e6937b1664","repo":"kubernetes/kops","slug":"invalid-key-value-pair-q-expected-separator-q","errorCode":null,"errorMessage":"invalid key/value pair %q (expected separator %q)","messagePattern":"invalid key/value pair %q \\(expected separator %q\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodeup/pkg/model/kubelet.go","lineNumber":527,"sourceCode":"\n// parseKeyValueList parses a comma-separated list of key/value pairs\n// separated by sep (for example \"memory.available<100Mi\" with sep=\"<\").\n// Whitespace around keys and values is trimmed. An empty input returns\n// (nil, nil) so callers can leave the corresponding kubelet config field\n// unset. Returns an error if any entry is missing the separator.\n//\n// The kops API uses these CSV strings for fields that kubelet represents\n// as map[string]string: eviction-hard / eviction-soft use \"<\", while\n// eviction-soft-grace-period and eviction-minimum-reclaim use \"=\".\nfunc parseKeyValueList(in string, sep string) (map[string]string, error) {\n\tif in == \"\" {\n\t\treturn nil, nil\n\t}\n\tresult := make(map[string]string, strings.Count(in, \",\")+1)\n\tfor kv := range strings.SplitSeq(in, \",\") {\n\t\tk, v, ok := strings.Cut(kv, sep)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"invalid key/value pair %q (expected separator %q)\", kv, sep)\n\t\t}\n\t\tresult[strings.TrimSpace(k)] = strings.TrimSpace(v)\n\t}\n\treturn result, nil\n}\n\n// parseTaint converts the kops \"key=value:Effect\" taint string (the form\n// historically passed to --register-with-taints) into a v1.Taint, the type\n// the kubelet config field RegisterWithTaints requires.\nfunc parseTaint(s string) (v1.Taint, error) {\n\tparsed, err := kopsutil.ParseTaint(s)\n\tif err != nil {\n\t\treturn v1.Taint{}, err\n\t}\n\treturn v1.Taint{\n\t\tKey:    parsed[\"key\"],\n\t\tValue:  parsed[\"value\"],\n\t\tEffect: v1.TaintEffect(parsed[\"effect\"]),","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/nodeup/pkg/model/kubelet.go#L509-L545","documentation":"parseKeyValueList splits a comma-separated list and each entry on the given separator ('<' or '='). If an entry does not contain the expected separator, it returns \"invalid key/value pair %q (expected separator %q)\", which callers wrap with the field name (evictionHard/evictionSoft/evictionSoftGracePeriod/evictionMinimumReclaim).","triggerScenarios":"parseKeyValueList -> strings.Cut(kv, sep) returns ok=false for some entry: e.g. parseKeyValueList(\"memory.available100Mi\", \"<\") or an empty segment from a trailing comma.","commonSituations":"Hand-editing kubelet eviction settings in the cluster spec; separator confusion between the '<' fields (evictionHard/evictionSoft) and '=' fields (grace period, minimum reclaim); trailing/leading commas creating separator-less empty entries.","solutions":["Fix the quoted entry to include the expected separator (see %q in the message)","Match separator to field: '<' for evictionHard/Soft, '=' for grace period/minimum reclaim","Remove stray commas / empty segments from the list","Validate the string with a local split before applying to the cluster"],"exampleFix":"// before\nparseKeyValueList(\"memory.available<100Mi,nodefs.available\", \"<\")\n// after\nparseKeyValueList(\"memory.available<100Mi,nodefs.available<10%\", \"<\")","handlingStrategy":"validation","validationCode":"func validateKVList(in, sep string) error {\n    if in == \"\" { return nil }\n    for _, kv := range strings.Split(in, \",\") {\n        if !strings.Contains(kv, sep) {\n            return fmt.Errorf(\"entry %q missing separator %q\", kv, sep)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := parseKeyValueList(userInput, \"<\"); err != nil {\n    var invalid *invalidKVError\n    if errors.As(err, &invalid) {\n        return fmt.Errorf(\"re-enter the list as comma-separated key<value pairs: %w\", err)\n    }\n    return err\n}","preventionTips":["Verify each comma-separated entry contains the expected separator before submitting","Use '<' for eviction thresholds, '=' for grace periods and minimum reclaims","Strip whitespace and trailing commas from hand-edited lists","Add a spec-validation step in CI before kops update cluster"],"tags":["config-parsing","kubelet","eviction"],"backgroundTag":"kubelet-eviction-config-invalid","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"}