kubernetes/kops · error

invalid key/value pair %q (expected separator %q)

Error message

invalid key/value pair %q (expected separator %q)

What it means

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).

Source

Thrown at nodeup/pkg/model/kubelet.go:527

// parseKeyValueList parses a comma-separated list of key/value pairs
// separated by sep (for example "memory.available<100Mi" with sep="<").
// Whitespace around keys and values is trimmed. An empty input returns
// (nil, nil) so callers can leave the corresponding kubelet config field
// unset. Returns an error if any entry is missing the separator.
//
// The kops API uses these CSV strings for fields that kubelet represents
// as map[string]string: eviction-hard / eviction-soft use "<", while
// eviction-soft-grace-period and eviction-minimum-reclaim use "=".
func parseKeyValueList(in string, sep string) (map[string]string, error) {
	if in == "" {
		return nil, nil
	}
	result := make(map[string]string, strings.Count(in, ",")+1)
	for kv := range strings.SplitSeq(in, ",") {
		k, v, ok := strings.Cut(kv, sep)
		if !ok {
			return nil, fmt.Errorf("invalid key/value pair %q (expected separator %q)", kv, sep)
		}
		result[strings.TrimSpace(k)] = strings.TrimSpace(v)
	}
	return result, nil
}

// parseTaint converts the kops "key=value:Effect" taint string (the form
// historically passed to --register-with-taints) into a v1.Taint, the type
// the kubelet config field RegisterWithTaints requires.
func parseTaint(s string) (v1.Taint, error) {
	parsed, err := kopsutil.ParseTaint(s)
	if err != nil {
		return v1.Taint{}, err
	}
	return v1.Taint{
		Key:    parsed["key"],
		Value:  parsed["value"],
		Effect: v1.TaintEffect(parsed["effect"]),

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Fix the quoted entry to include the expected separator (see %q in the message)
  2. Match separator to field: '<' for evictionHard/Soft, '=' for grace period/minimum reclaim
  3. Remove stray commas / empty segments from the list
  4. Validate the string with a local split before applying to the cluster

Example fix

// before
parseKeyValueList("memory.available<100Mi,nodefs.available", "<")
// after
parseKeyValueList("memory.available<100Mi,nodefs.available<10%", "<")
Defensive patterns

Strategy: validation

Validate before calling

func validateKVList(in, sep string) error {
    if in == "" { return nil }
    for _, kv := range strings.Split(in, ",") {
        if !strings.Contains(kv, sep) {
            return fmt.Errorf("entry %q missing separator %q", kv, sep)
        }
    }
    return nil
}

Try / catch

if _, err := parseKeyValueList(userInput, "<"); err != nil {
    var invalid *invalidKVError
    if errors.As(err, &invalid) {
        return fmt.Errorf("re-enter the list as comma-separated key<value pairs: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: parseKeyValueList -> strings.Cut(kv, sep) returns ok=false for some entry: e.g. parseKeyValueList("memory.available100Mi", "<") or an empty segment from a trailing comma.

Common situations: 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.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/7fc3a5e6937b1664. Report an issue: GitHub.