derailed/k9s · error
unable to forward port because pod is not running. Current s
Error message
unable to forward port because pod is not running. Current status=%v
What it means
Start rejects the session unless the target pod's status.phase is Running. Pending, Succeeded, Failed, or Unknown pods cannot serve a tunnel, so the DAO fails fast and includes the observed phase in the message.
Source
Thrown at internal/dao/port_forwarder.go:141
ns, n := client.Namespaced(path)
auth, err := p.Client().CanI(ns, client.PodGVR, n, client.GetAccess)
if err != nil {
return nil, err
}
if !auth {
return nil, fmt.Errorf("user is not authorized to get pods")
}
podName := strings.Split(n, "|")[0]
var res Pod
res.Init(p, client.PodGVR)
pod, err := res.GetInstance(client.FQN(ns, podName))
if err != nil {
return nil, err
}
if pod.Status.Phase != v1.PodRunning {
return nil, fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
}
auth, err = p.Client().CanI(ns, client.PodGVR.WithSubResource("portforward"), "", []string{client.CreateVerb})
if err != nil {
return nil, err
}
if !auth {
return nil, fmt.Errorf("user is not authorized to update portforward")
}
cfg, err := p.Client().RestConfig()
if err != nil {
return nil, err
}
cfg.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
cfg.APIPath = "/api"
codec, _ := codec()
cfg.NegotiatedSerializer = codec.WithoutConversion()View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Wait for readiness: kubectl wait --for=condition=ready pod/<name> --timeout=90s
- Diagnose why it is not Running: kubectl describe pod <name> (events, init containers, probes)
- For completed Job pods, run a new pod instead of forwarding to the finished one
Example fix
// before
pf, err := pfMgr.Start(path, tunnel) // fails on Pending pod
// after
pod, _ := podDAO.GetInstance(fqn)
if pod == nil || pod.Status.Phase != v1.PodRunning {
return fmt.Errorf("wait for pod %q to be Running before forwarding (phase=%v)", fqn, pod.Status.Phase)
}
pf, err := pfMgr.Start(path, tunnel) Defensive patterns
Strategy: validation
Validate before calling
var res dao.Pod
res.Init(client, client.PodGVR)
pod, err := res.GetInstance(fqn)
if err != nil { return err }
if pod.Status.Phase != v1.PodRunning {
return fmt.Errorf("pod phase %s; wait for Running", pod.Status.Phase)
} Type guard
func isRunnable(p *v1.Pod) bool {
return p != nil && p.Status.Phase == v1.PodRunning
} Try / catch
if _, err := pf.Start(path, tunnel); err != nil {
if strings.Contains(err.Error(), "pod is not running") {
// wait for ready condition, then retry Start once
}
} Prevention
- Gate forwards on readiness conditions, not just on the pod existing
- For Job pods, check status.phase != Succeeded before offering forward
- Retrigger forwards after rollout events instead of keeping stale tunnels
When it happens
Trigger: Forwarding while the pod is Pending (image pull, scheduling, init containers), Succeeded/Failed (completed Job pods), or Unknown (node unreachable).
Common situations: Forwarding immediately after a rollout before readiness; forwarding to one-shot Job pods that already finished; node NotReady leaving pods in Unknown.
Related errors
- no exposed ports
- no port number assigned
- user is not authorized to get pods
- user is not authorized to update portforward
- invalid annotation %q
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/4b52050c7c754564.
Report an issue: GitHub.