kubernetes/kops · error

cannot decode GCE label: %q

Error message

cannot decode GCE label: %q

What it means

DecodeGCELabel reverses EncodeGCELabel, which URI-escapes non [0-9a-z] bytes with '-' instead of '%'. Decoding turns '-' back into '%' and calls url.QueryUnescape; if that fails (e.g. a trailing or lone '%' after replacement, or an invalid hex escape like %G1), the function wraps the input string in this error. It signals that the label value was not produced by EncodeGCELabel or has been corrupted.

Source

Thrown at upup/pkg/fi/cloudup/gce/labels.go:64

		c := s[i]
		if ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') {
			b.WriteByte(c)
		} else {
			b.WriteByte('-')
			b.WriteByte("0123456789abcdef"[c>>4])
			b.WriteByte("0123456789abcdef"[c&15])
		}
	}

	return b.String()
}

// DecodeGCELabel reverse EncodeGCELabel, taking the encoded RFC1035 compatible value back to a string
func DecodeGCELabel(s string) (string, error) {
	uriForm := strings.ReplaceAll(s, "-", "%")
	v, err := url.QueryUnescape(uriForm)
	if err != nil {
		return "", fmt.Errorf("cannot decode GCE label: %q", s)
	}
	return v, nil
}

// TagForRole return the instance (network) tag used for instances with the given role.
func TagForRole(clusterName string, role kops.InstanceGroupRole) string {
	return ClusterPrefixedName(GceLabelNameRolePrefix+role.ToLowerString(), clusterName, 63)
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure every label was written via EncodeGCELabel; re-encode any manually-set labels or let kops recreate them
  2. Inspect the failing label string: any '-' must decode to '%XX' with two valid hex digits; fix or remove invalid '-' sequences
  3. If labels came from an older kops version, upgrade kops and run 'kops update cluster' to re-stamp labels
  4. Wrap decode failures and skip/log the offending label instead of failing the whole etcd status scan

Example fix

// before
v, err := url.QueryUnescape(uriForm)
if err != nil {
	return "", fmt.Errorf("cannot decode GCE label: %q", s)
}
// after
// only attempt decode for kops-encoded labels; otherwise return as-is
if !strings.Contains(s, "-") {
	return s, nil
}
v, err := url.QueryUnescape(uriForm)
if err != nil {
	return "", fmt.Errorf("cannot decode GCE label: %q", s)
}
Defensive patterns

Strategy: validation

Validate before calling

func isDecodableGCELabel(s string) bool {
	uriForm := strings.ReplaceAll(s, "-", "%")
	_, err := url.QueryUnescape(uriForm)
	return err == nil
}
if !isDecodableGCELabel(label) { log.Warnf("skipping non-kops label %q", label); return }

Type guard

func isKopsEncodedLabel(s string) bool {
	// every '-' must be followed by two hex digits (from EncodeGCELabel escaping)
	for i := 0; i < len(s); i++ {
		if s[i] == '-' && (i+2 >= len(s) || !isHex(s[i+1]) || !isHex(s[i+2])) {
			return false
		}
	}
	return true
}

Try / catch

decoded, err := gce.DecodeGCELabel(label)
if err != nil {
	// treat as foreign/corrupt label: skip rather than fail the scan
	klog.V(2).Infof("ignoring undecodable label %q: %v", label, err)
	continue
}

Prevention

When it happens

Trigger: A GCE label value containing '-' that was created manually or by another tool (kops-generated labels always encode literal '-' as '--2d', so a lone '-' decodes to '%2d' and is fine only when followed by two hex digits). Passing a raw '-'-containing string like 'my-label' yields '%2d' which is valid, but 'abc%-' or a string with a single trailing '-' producing an incomplete percent-escape (e.g. 'x-1' -> 'x%1') fails QueryUnescape and triggers this error. Called from findEtcdStatus when reading etcd cluster status labels off GCE instances.

Common situations: Manually editing GCE labels in the cloud console; labels created by other controllers or older kops versions with different encoding; hand-crafting instance group names/labels with characters that produce invalid escapes; copied/mangled label strings in scripts.

Related errors


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