derailed/k9s · error
invalid plain port-forward %s
Error message
invalid plain port-forward %s
What it means
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.
Source
Thrown at internal/port/pf.go:44
)
// PFAnn represents a portforward annotation value.
// Shape: container/portname|portNum:localPort
type PFAnn struct {
Container string
ContainerPort intstr.IntOrString
LocalPort string
containerPortNum string
}
func ParsePlainPF(ann string) (*PFAnn, error) {
if ann == "" {
return nil, fmt.Errorf("invalid annotation %q", ann)
}
var pf PFAnn
mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann))
if len(mm) < 3 {
return nil, fmt.Errorf("invalid plain port-forward %s", ann)
}
if mm[2] == "" {
pf.ContainerPort = intstr.Parse(mm[1])
pf.LocalPort = mm[1]
return &pf, nil
}
pf.LocalPort, pf.ContainerPort = mm[1], intstr.Parse(mm[2])
return &pf, nil
}
// ParsePF hydrate a portforward annotation from string.
func ParsePF(ann string) (*PFAnn, error) {
if pf, err := ParsePlainPF(ann); err == nil {
return pf, nil
}
var pf PFAnn
if mm := pfPlainRX.FindStringSubmatch(strings.TrimSpace(ann)); len(mm) == 3 {View on GitHub (pinned to 2d3ccc6ba2)
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
Example fix
# before (single colon + name is not the plain form) k9scli.io/port-forwards: "svc:8080" # after k9scli.io/port-forwards: "svc::8080:80"
Defensive patterns
Strategy: validation
Validate before calling
var plainPFRX = regexp.MustCompile(`\A(\d*):(\d*|[\w-]*)\z`)
func validPlainPF(ann string) bool {
return plainPFRX.MatchString(strings.TrimSpace(ann))
} Try / catch
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.
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- no exposed ports
- no port number assigned
- invalid annotation %q
- invalid port-forward specification %s
- unable to parse selection %s
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/a7e289ba7799c1d2.
Report an issue: GitHub.