derailed/k9s · error

no port number assigned

Error message

no port number assigned

What it means

PFAnn.PortNum resolves the container side of a port-forward tunnel. If ContainerPort is an int it stringifies it; otherwise (intstr.String, i.e. a named port) it needs containerPortNum — the numeric value captured earlier from an explicit annotation token (pf.go:63/77) or from the matched port descriptor (pf.go:90). A named port with no resolved number leaves containerPortNum empty and PortNum fails, which aborts PFAnn.ToTunnel.

Source

Thrown at internal/port/pf.go:119

		return s + p.containerPortNum
	}
	return s + p.LocalPort
}

// String dumps the annotation.
func (p *PFAnn) String() string {
	return p.Container + "::" + p.LocalPort + ":" + p.containerPortNum
}

func (p *PFAnn) PortNum() (string, error) {
	if p.ContainerPort.Type == intstr.Int {
		return p.ContainerPort.String(), nil
	}
	if p.containerPortNum != "" {
		return p.containerPortNum, nil
	}

	return "", errors.New("no port number assigned")
}

func (p *PFAnn) ToTunnel(address string) (PortTunnel, error) {
	var pt PortTunnel
	port, err := p.PortNum()
	if err != nil {
		return pt, err
	}

	pt.Address, pt.Container = address, p.Container
	pt.ContainerPort, pt.LocalPort = port, p.LocalPort

	return pt, nil
}

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Use the numeric container port in the annotation: container::localPort:9090 instead of a port name.
  2. If using a named port, verify the name exists in that container's ports[] list so the resolver can map it to its number.
  3. Check the annotation syntax matches 'container::local:container' — mis-parsed tokens leave containerPortNum empty.

Example fix

# before
k9s.io/portforward: "app::8080:http"   # 'http' unresolvable -> no port number assigned
# after
k9s.io/portforward: "app::8080:8080"
Defensive patterns

Strategy: validation

Validate before calling

// resolve the number before building the tunnel
if _, err := strconv.Atoi(ann.ContainerPort.String()); err != nil {
    // named port: ensure it was matched to a numeric containerPortNum
    if n, err := ann.PortNum(); err != nil {
        _ = n // refuse: annotation must use a numeric container port
        return fmt.Errorf("port %q has no number; use container::local:num", ann.ContainerPort)
    }
}

Type guard

func hasPortNum(p *port.PFAnn) bool {
    _, err := p.PortNum()
    return err == nil
}

Try / catch

t, err := ann.ToTunnel(address)
if err != nil && strings.Contains(err.Error(), "no port number assigned") {
    // rewrite annotation with a numeric container port and retry
}

Prevention

When it happens

Trigger: A k9s.io/portforward annotation or descriptor referring to a named (string) container port whose numeric value was never resolved because the container spec lookup failed or the annotation used a name that does not exist; then building the tunnel with ToTunnel.

Common situations: Hand-written port-forward annotations using port names instead of numbers; referring to a name defined in a different container than the one in the annotation; races where the pod spec is not yet cached.

Related errors


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