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
- Use the numeric container port in the annotation: container::localPort:9090 instead of a port name.
- If using a named port, verify the name exists in that container's ports[] list so the resolver can map it to its number.
- 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
- Prefer numeric container ports in k9s.io/portforward annotations.
- When using named ports, confirm the name exists in that container's ports[].
- Follow the 'container::localPort:containerPort' annotation syntax exactly.
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
- no exposed ports
- revision conversion failed
- invalid annotation %q
- invalid plain port-forward %s
- invalid port-forward specification %s
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/d06ad675e780a406.
Report an issue: GitHub.