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

  1. Use the plain form 'localPort' or 'localPort:containerPort' where containerPort is digits or a named port
  2. If a container name is required, switch to the full spec 'container::local:containerPort' parsed by ParsePF
  3. Strip spaces, slashes and protocol suffixes from the annotation value
  4. 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

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


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/a7e289ba7799c1d2. Report an issue: GitHub.