{"record":{"id":"9dd88aac140137aa","repo":"kubernetes/kops","slug":"cannot-decode-gce-label-q","errorCode":null,"errorMessage":"cannot decode GCE label: %q","messagePattern":"cannot decode GCE label: %q","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gce/labels.go","lineNumber":64,"sourceCode":"\t\tc := s[i]\n\t\tif ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') {\n\t\t\tb.WriteByte(c)\n\t\t} else {\n\t\t\tb.WriteByte('-')\n\t\t\tb.WriteByte(\"0123456789abcdef\"[c>>4])\n\t\t\tb.WriteByte(\"0123456789abcdef\"[c&15])\n\t\t}\n\t}\n\n\treturn b.String()\n}\n\n// DecodeGCELabel reverse EncodeGCELabel, taking the encoded RFC1035 compatible value back to a string\nfunc DecodeGCELabel(s string) (string, error) {\n\turiForm := strings.ReplaceAll(s, \"-\", \"%\")\n\tv, err := url.QueryUnescape(uriForm)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot decode GCE label: %q\", s)\n\t}\n\treturn v, nil\n}\n\n// TagForRole return the instance (network) tag used for instances with the given role.\nfunc TagForRole(clusterName string, role kops.InstanceGroupRole) string {\n\treturn ClusterPrefixedName(GceLabelNameRolePrefix+role.ToLowerString(), clusterName, 63)\n}\n","sourceCodeStart":46,"sourceCodeEnd":73,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gce/labels.go#L46-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every label was written via EncodeGCELabel; re-encode any manually-set labels or let kops recreate them","Inspect the failing label string: any '-' must decode to '%XX' with two valid hex digits; fix or remove invalid '-' sequences","If labels came from an older kops version, upgrade kops and run 'kops update cluster' to re-stamp labels","Wrap decode failures and skip/log the offending label instead of failing the whole etcd status scan"],"exampleFix":"// before\nv, err := url.QueryUnescape(uriForm)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"cannot decode GCE label: %q\", s)\n}\n// after\n// only attempt decode for kops-encoded labels; otherwise return as-is\nif !strings.Contains(s, \"-\") {\n\treturn s, nil\n}\nv, err := url.QueryUnescape(uriForm)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"cannot decode GCE label: %q\", s)\n}","handlingStrategy":"validation","validationCode":"func isDecodableGCELabel(s string) bool {\n\turiForm := strings.ReplaceAll(s, \"-\", \"%\")\n\t_, err := url.QueryUnescape(uriForm)\n\treturn err == nil\n}\nif !isDecodableGCELabel(label) { log.Warnf(\"skipping non-kops label %q\", label); return }","typeGuard":"func isKopsEncodedLabel(s string) bool {\n\t// every '-' must be followed by two hex digits (from EncodeGCELabel escaping)\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] == '-' && (i+2 >= len(s) || !isHex(s[i+1]) || !isHex(s[i+2])) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","tryCatchPattern":"decoded, err := gce.DecodeGCELabel(label)\nif err != nil {\n\t// treat as foreign/corrupt label: skip rather than fail the scan\n\tklog.V(2).Infof(\"ignoring undecodable label %q: %v\", label, err)\n\tcontinue\n}","preventionTips":["Only ever write GCE labels through EncodeGCELabel, never manually in the console","Reject instance group / label inputs containing characters outside RFC1035-safe sets at spec-validation time","When scanning labels, skip-and-log labels that fail decode instead of aborting","Add unit tests mixing hand-written labels to ensure decode is only attempted for encoded values"],"tags":["gce","labels","url-decoding","encoding"],"backgroundTag":"invalid-label-encoding","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}