derailed/k9s · error
port %s is not available on host
Error message
port %s is not available on host
What it means
PortTunnels.CheckAvailable re-validates an already-built tunnel list: for each tunnel it bind-tests (Address, LocalPort) via net.Listen (IsPortFree) and reports the first port that cannot be bound. Same root cause as error 162 but hit on saved/replayed tunnels rather than freshly parsed annotations.
Source
Thrown at internal/port/tunnel.go:22
package port
import (
"context"
"fmt"
"log/slog"
"net"
"github.com/derailed/k9s/internal/slogs"
)
// PortTunnels represents a collection of tunnels.
type PortTunnels []PortTunnel
// CheckAvailable checks if all port tunnels are available.
func (t PortTunnels) CheckAvailable(ctx context.Context) error {
for _, pt := range t {
if !IsPortFree(ctx, pt) {
return fmt.Errorf("port %s is not available on host", pt.LocalPort)
}
}
return nil
}
// PortTunnel represents a host tunnel port mapper.
type PortTunnel struct {
Address, Container, LocalPort, ContainerPort string
}
// NewPortTunnel returns a new instance.
func NewPortTunnel(a, co, lp, cp string) PortTunnel {
return PortTunnel{
Address: a,
Container: co,
LocalPort: lp,
ContainerPort: cp,View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Identify the holder: lsof -i :<port> (or ss -ltnp | grep <port>) and stop it
- Change the tunnel's local port to a free one before re-checking
- Close other k9s sessions using the same ports
- Wait out TIME_WAIT (usually ~60s) if the previous listener just closed
Example fix
# before: saved tunnel 127.0.0.1:8080 blocked by stale process lsof -ti :8080 | xargs kill # after: re-run CheckAvailable -> passes
Defensive patterns
Strategy: fallback
Validate before calling
// Identify ALL conflicts up front (CheckAvailable stops at the first):
for _, pt := range tunnels {
if !port.IsPortFree(ctx, pt) {
log.Printf("conflict: %s", pt.String())
}
} Try / catch
Catch the per-tunnel error, report the conflicting LocalPort, and either skip that tunnel or remap it to a free port; continue with the rest of the list.
Prevention
- Bind-test saved tunnels right after restore, before the UI needs them
- Run one k9s instance per workstation or partition local-port ranges per instance
- Prefer ephemeral ports for anything replayed from saved state
When it happens
Trigger: Reconnecting port-forwards saved from a previous session whose local ports have since been taken; a race where another process binds the port between tunnel creation and the availability check; address not bindable (e.g. configured address not on host).
Common situations: Two k9s instances on the same workstation sharing fixed ports; a crashed session leaving sockets in TIME_WAIT or listeners behind; VPN/container tooling grabbing ports after resume.
Related errors
- port %s is not available on host
- no exposed ports
- no port number assigned
- unable to connect to context %q
- failed to maintain log stream after %d retries
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/7936bdadca81b1c8.
Report an issue: GitHub.