{"record":{"id":"7936bdadca81b1c8","repo":"derailed/k9s","slug":"port-s-is-not-available-on-host-7936bd","errorCode":null,"errorMessage":"port %s is not available on host","messagePattern":"port (.+?) is not available on host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/port/tunnel.go","lineNumber":22,"sourceCode":"package port\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net\"\n\n\t\"github.com/derailed/k9s/internal/slogs\"\n)\n\n// PortTunnels represents a collection of tunnels.\ntype PortTunnels []PortTunnel\n\n// CheckAvailable checks if all port tunnels are available.\nfunc (t PortTunnels) CheckAvailable(ctx context.Context) error {\n\tfor _, pt := range t {\n\t\tif !IsPortFree(ctx, pt) {\n\t\t\treturn fmt.Errorf(\"port %s is not available on host\", pt.LocalPort)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// PortTunnel represents a host tunnel port mapper.\ntype PortTunnel struct {\n\tAddress, Container, LocalPort, ContainerPort string\n}\n\n// NewPortTunnel returns a new instance.\nfunc NewPortTunnel(a, co, lp, cp string) PortTunnel {\n\treturn PortTunnel{\n\t\tAddress:       a,\n\t\tContainer:     co,\n\t\tLocalPort:     lp,\n\t\tContainerPort: cp,","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/port/tunnel.go#L4-L40","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before: saved tunnel 127.0.0.1:8080 blocked by stale process\nlsof -ti :8080 | xargs kill\n# after: re-run CheckAvailable -> passes","handlingStrategy":"fallback","validationCode":"// Identify ALL conflicts up front (CheckAvailable stops at the first):\nfor _, pt := range tunnels {\n\tif !port.IsPortFree(ctx, pt) {\n\t\tlog.Printf(\"conflict: %s\", pt.String())\n\t}\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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"],"tags":["port-forward","network","port-in-use"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}