{"record":{"id":"8ee579bb4208e473","repo":"derailed/k9s","slug":"invalid-annotation-q","errorCode":null,"errorMessage":"invalid annotation %q","messagePattern":"invalid annotation %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/port/pf.go","lineNumber":39,"sourceCode":")\n\nvar (\n\tpfRX      = regexp.MustCompile(`\\A([\\w-]+)::(\\d*):?(\\d*|[\\w-]*)/?(\\d+)?\\z`)\n\tpfPlainRX = regexp.MustCompile(`\\A(\\d*):?(\\d*|[\\w-]*)\\z`)\n)\n\n// PFAnn represents a portforward annotation value.\n// Shape: container/portname|portNum:localPort\ntype PFAnn struct {\n\tContainer        string\n\tContainerPort    intstr.IntOrString\n\tLocalPort        string\n\tcontainerPortNum string\n}\n\nfunc ParsePlainPF(ann string) (*PFAnn, error) {\n\tif ann == \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid annotation %q\", ann)\n\t}\n\tvar pf PFAnn\n\tmm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann))\n\tif len(mm) < 3 {\n\t\treturn nil, fmt.Errorf(\"invalid plain port-forward %s\", ann)\n\t}\n\tif mm[2] == \"\" {\n\t\tpf.ContainerPort = intstr.Parse(mm[1])\n\t\tpf.LocalPort = mm[1]\n\t\treturn &pf, nil\n\t}\n\tpf.LocalPort, pf.ContainerPort = mm[1], intstr.Parse(mm[2])\n\n\treturn &pf, nil\n}\n\n// ParsePF hydrate a portforward annotation from string.\nfunc ParsePF(ann string) (*PFAnn, error) {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/port/pf.go#L21-L57","documentation":"ParsePlainPF parses the k9s port-forward annotation shorthand (plain form: 'port' or 'local:remote' / 'port:name', full form 'container::local:remote'). It rejects an annotation value that is empty before any regex matching, because an empty string cannot carry a port pair.","triggerScenarios":"A pod/service carries the k9scli.io/port-forwards or k9scli.io/auto-port-forwards annotation but its value is '' (or whitespace-only after trimming in the regex path): e.g. `k9scli.io/port-forwards: \"\"` set by a template variable that rendered empty.","commonSituations":"Helm templates defaulting the annotation to an empty string instead of omitting it; CI pipelines injecting the annotation key unconditionally; manual copy-paste leaving the value blank.","solutions":["Remove the empty annotation: kubectl annotate pod <name> k9scli.io/port-forwards-","Or set a valid value: plain '8080' (same local/remote), '9090:8080' (local:remote), or 'container::9090:8080'","In Helm templates, guard with {{- if .Values.pf }} so the annotation is only emitted when non-empty"],"exampleFix":"# before: empty annotation\nmetadata:\n  annotations:\n    k9scli.io/port-forwards: \"\"\n# after: either omit the key or set a valid value\nmetadata:\n  annotations:\n    k9scli.io/port-forwards: \"9090:8080\"","handlingStrategy":"validation","validationCode":"// Skip empty annotation values before parsing.\nfor _, key := range []string{port.K9sPortForwardsKey, port.K9sAutoPortForwardsKey} {\n    v := strings.TrimSpace(anns[key])\n    if anns[key] != \"\" && v == \"\" {\n        return fmt.Errorf(\"annotation %s is whitespace-only; remove it or set e.g. '9090:8080'\", key)\n    }\n    if v == \"\" { continue }\n    pf, err := port.ParsePlainPF(v)\n    ...\n}","typeGuard":"func validPFShape(v string) bool {\n    v = strings.TrimSpace(v)\n    if v == \"\" { return false }\n    mm := regexp.MustCompile(`\\A(\\d*):?(\\d*|[\\w-]*)\\z`).FindStringSubmatch(v)\n    return len(mm) >= 3 && mm[0] == v\n}","tryCatchPattern":"pf, err := port.ParsePlainPF(ann)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid annotation\") {\n        // tolerate and skip: an empty annotation must not break port-forward discovery\n        slog.Warn(\"ignoring empty k9s port-forward annotation\", slogs.GVR, gvr)\n        continue\n    }\n    return err\n}","preventionTips":["In Helm/manifest templates, emit the k9s annotation only when the value is set ({{- if }})","Validate annotation values in CI linting: k9scli.io/port-forwards must match port, local:remote, or container::local:remote"],"tags":["kubernetes","port-forward","annotation","configuration","k9s"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}