derailed/k9s · error
invalid port-forward specification %s
Error message
invalid port-forward specification %s
What it means
Thrown by port.ParsePF when the annotation matches neither the plain form nor the full form regex \A([\w-]+)::(\d*):(\d*|[\w-]*)/?(\d+)?\z, i.e. 'container::localPort:containerPort[/portNum]'. It is the generic 'not a port-forward spec k9s understands' error.
Source
Thrown at internal/port/pf.go:67
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 {
pf.containerPortNum = mm[0]
}
r := pfRX.FindStringSubmatch(strings.TrimSpace(ann))
if len(r) < 4 {
return &pf, fmt.Errorf("invalid port-forward specification %s", ann)
}
pf.Container = r[1]
pf.LocalPort, pf.ContainerPort = r[2], intstr.Parse(r[3])
if r[3] == "" {
pf.ContainerPort = intstr.Parse(pf.LocalPort)
}
// Testing only!
if len(r) == 5 && r[4] != "" {
pf.containerPortNum = r[4]
}
if pf.LocalPort == "" {
pf.LocalPort = pf.containerPortNum
}
return &pf, nil
}
View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Use the full form 'container::localPort:containerPort', e.g. 'mysvc::8080:9090'
- Keep the container segment to letters, digits, underscore and hyphen only (no '/', '.', ':')
- Keep the local port numeric; named ports are only valid for the container port
- Print the raw annotation (kubectl get <res> -o jsonpath='{.metadata.annotations.k9scli\.io/port-forwards}') and eyeball it against the two accepted forms
Example fix
# before k9scli.io/port-forwards: "mysvc:8080:9090" # after k9scli.io/port-forwards: "mysvc::8080:9090"
Defensive patterns
Strategy: validation
Validate before calling
var (
plainPFRX = regexp.MustCompile(`\A(\d*):(\d*|[\w-]*)\z`)
fullPFRX = regexp.MustCompile(`\A([\w-]+)::(\d*):(\d*|[\w-]*)/?(\d+)?\z`)
)
func validPF(ann string) bool {
s := strings.TrimSpace(ann)
return plainPFRX.MatchString(s) || fullPFRX.MatchString(s)
} Try / catch
On error from port.ParsePF, wrap with context (fmt.Errorf("parsing port-forward annotation %q: %w", ann, err)) and surface to the user as a config hint; do not retry — the string is deterministic. Prevention
- Generate annotations from templates that interpolate validated container/port variables
- Document '::' vs ':' distinction wherever port-forwards are configured
- Unit-test both accepted forms plus common malformed forms in your tooling
When it happens
Trigger: Values like 'svc:8080:9090' (single colon before container segment), '::8080:80' (empty container, [\w-]+ requires at least one character), 'ns/svc::8080' (slash in container name), or '8080:80:90' (three colon-separated fields with no '::').
Common situations: Typos between ':' and '::' in hand-written annotations; copying 'namespace/service:port' kubectl syntax; k9s version changes that tightened the accepted grammar; annotation placed under the wrong key so unrelated text is parsed.
Related errors
- no exposed ports
- no port number assigned
- invalid annotation %q
- invalid plain port-forward %s
- unable to parse selection %s
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/df1bf37aef9e22e0.
Report an issue: GitHub.