derailed/k9s · error

no exposed ports

Error message

no exposed ports

What it means

Annotations.PreferredPorts decides which container port to port-forward: with no k9s.io/portforward annotation it defaults to the first declared container port. If the container spec declares zero ports (specs is empty) there is nothing to forward and k9s refuses rather than guessing. Only an explicit annotation with an explicit port can override this.

Source

Thrown at internal/port/ann.go:14

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s

package port

import (
	"errors"
)

type Annotations map[string]string

func (a Annotations) PreferredPorts(specs ContainerPortSpecs) (PFAnns, error) {
	if len(specs) == 0 {
		return nil, errors.New("no exposed ports")
	}

	value, ok := a[K9sPortForwardsKey]
	if !ok {
		return PFAnns{specs[0].ToPFAnn()}, nil
	}

	return specs.MatchAnnotations(value), nil
}

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Annotate the pod to declare the port explicitly: kubectl annotate pod <p> k9s.io/portforward="container::localPort:containerPort" so PreferredPorts uses MatchAnnotations instead of the empty spec list.
  2. Or add containerPort entries to the workload spec if you own it.
  3. Port-forward via the Service instead, where targetPort mapping is known.

Example fix

# before: container has no ports -> "no exposed ports"
spec:
  containers:
    - name: app
      image: app:dev
# after: declare the listening port
spec:
  containers:
    - name: app
      image: app:dev
      ports:
        - containerPort: 8080
Defensive patterns

Strategy: validation

Validate before calling

if len(specs) == 0 {
    // cannot pick a default port; require an explicit annotation
    if ann, ok := podAnns[port.K9sPortForwardsKey]; ok {
        _ = ann // use annotation path
    } else {
        return errors.New("container exposes no ports; annotate or add containerPort")
    }
}

Type guard

func hasExposedPorts(specs port.ContainerPortSpecs) bool { return len(specs) > 0 }

Prevention

When it happens

Trigger: Launching a port-forward on a pod whose container spec has no ports[] entries and no k9s port-forward annotation; containers that receive traffic only via Service port targeting or env-configured listeners.

Common situations: Port-forwarding helper/sidecar containers (e.g. log shippers) with no declared ports; apps where the Dockerfile EXPOSE was not translated into containerPort; Istio-injected proxies in the list.

Related errors


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