{"record":{"id":"a7e289ba7799c1d2","repo":"derailed/k9s","slug":"invalid-plain-port-forward-s","errorCode":null,"errorMessage":"invalid plain port-forward %s","messagePattern":"invalid plain port-forward (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/port/pf.go","lineNumber":44,"sourceCode":")\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) {\n\tif pf, err := ParsePlainPF(ann); err == nil {\n\t\treturn pf, nil\n\t}\n\tvar pf PFAnn\n\tif mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann)); len(mm) == 3 {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/port/pf.go#L26-L62","documentation":"Thrown by port.ParsePlainPF when a k9s port-forward annotation value does not match the plain form 'localPort[:containerPortOrName]' (regex \\A(\\d*):(\\d*|[\\w-]*)\\z, anchored). k9s parses values of the k9scli.io/port-forwards and k9scli.io/auto-port-forwards annotations with it. Any shape outside digits, one optional colon, and a word/hyphen container port is rejected.","triggerScenarios":"Calling ParsePlainPF with a value that contains '::' (e.g. 'svc::8080:9090'), a container name joined by a single colon (e.g. 'svc:8080'), interior spaces, or protocol suffixes like '8080/tcp'. FindStringSubmatch returns nil, so len(mm) < 3 and the error fires.","commonSituations":"Hand-editing the port-forwards annotation and forgetting that the container-qualified form needs '::'; pasting kubectl port-forward syntax into an extended-spec context; values corrupted by YAML block scalars or trailing comments.","solutions":["Use the plain form 'localPort' or 'localPort:containerPort' where containerPort is digits or a named port","If a container name is required, switch to the full spec 'container::local:containerPort' parsed by ParsePF","Strip spaces, slashes and protocol suffixes from the annotation value","If both forms are rejected, look for the companion error 'invalid port-forward specification' from ParsePF"],"exampleFix":"# before (single colon + name is not the plain form)\nk9scli.io/port-forwards: \"svc:8080\"\n# after\nk9scli.io/port-forwards: \"svc::8080:80\"","handlingStrategy":"validation","validationCode":"var plainPFRX = regexp.MustCompile(`\\A(\\d*):(\\d*|[\\w-]*)\\z`)\n\nfunc validPlainPF(ann string) bool {\n\treturn plainPFRX.MatchString(strings.TrimSpace(ann))\n}","typeGuard":null,"tryCatchPattern":"Treat any error from port.ParsePlainPF as user-config data: log the annotation value with the error and skip that single entry instead of failing the whole PFAnns slice.","preventionTips":["Pre-validate annotation values with the same regex before writing them via kubectl apply","Prefer the full 'container::local:containerPort' spec when a container name is needed","Keep annotation samples in chart tests so typos fail CI, not the terminal"],"tags":["port-forward","annotation","regex","kubernetes"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}