{"record":{"id":"8908120c2e425730","repo":"derailed/k9s","slug":"port-s-is-not-available-on-host","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/pfs.go","lineNumber":44,"sourceCode":"\t\t\t}\n\t\t}\n\t\tif a.LocalPort != \"\" {\n\t\t\tlps = append(lps, a.LocalPort)\n\t\t}\n\t}\n\n\treturn strings.Join(specs, \",\"), strings.Join(lps, \",\")\n}\n\nfunc (aa PFAnns) ToTunnels(address string, _ ContainerPortSpecs, available PortChecker) (PortTunnels, error) {\n\tpts := make(PortTunnels, 0, len(aa))\n\tfor _, a := range aa {\n\t\tpt, err := a.ToTunnel(address)\n\t\tif err != nil {\n\t\t\treturn pts, err\n\t\t}\n\t\tif !available(context.Background(), pt) {\n\t\t\treturn pts, fmt.Errorf(\"port %s is not available on host\", pt.LocalPort)\n\t\t}\n\t\tpts = append(pts, pt)\n\t}\n\n\treturn pts, nil\n}\n\n// ParsePFs hydrates a collection of portforward annotations.\nfunc ParsePFs(ann string) (PFAnns, error) {\n\tss := strings.Split(ann, \",\")\n\tpp := make(PFAnns, 0, len(ss))\n\tfor _, s := range ss {\n\t\tf, err := ParsePF(s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpp = append(pp, f)\n\t}","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/port/pfs.go#L26-L62","documentation":"PFAnns.ToTunnels builds one host tunnel per parsed annotation and, before accepting it, asks the injected PortChecker (which wraps IsPortFree, a net.Listen bind test) whether address:LocalPort can be bound. The error means the local port is already taken on the host, so a port-forward would fail.","triggerScenarios":"Forwarding the same pod/service twice in one k9s session; a local dev server already listening on the annotation's local port; a stale k9s process holding the port; two annotations claiming the same local port.","commonSituations":"Fixed local ports in k9scli.io/port-forwards colliding with local services (e.g. 3000, 8080, 5000 on macOS where AirPlay listens); resuming a session whose tunnels were never torn down; Docker/other container runtimes bound to the same port.","solutions":["Free the port: lsof -ti :<port> | xargs kill (or stop the local service using it)","Change the local port in the annotation, e.g. '8080:80' -> '18080:80'","Stop the duplicate port-forward in k9s (port-forward view, Ctrl-D / delete) before starting another","Bind to a different address or pick an ephemeral high port that nothing else uses"],"exampleFix":"# before: local 8080 already in use\nk9scli.io/port-forwards: \"myapp::8080:80\"\n# after: remap to free local port\nk9scli.io/port-forwards: \"myapp::18080:80\"","handlingStrategy":"fallback","validationCode":"// Pre-check each local port before building tunnels:\nif !port.IsPortFree(ctx, port.NewPortTunnel(\"127.0.0.1\", \"\", localPort, \"\")) {\n\tlocalPort = nextFreePort(localPort) // pick 18080, 18081, ...\n}","typeGuard":null,"tryCatchPattern":"On 'port %s is not available on host', free the port or remap the local port and rebuild PFAnns with the substituted value; treat as retryable-with-change, not retry-as-is.","preventionTips":["Use high ephemeral local ports (>20000) in annotations to avoid common service ranges","Check lsof/ss for the intended local port before starting k9s sessions","Avoid forwarding the same resource twice; close stale tunnels from the port-forward view"],"tags":["port-forward","network","port-in-use","kubernetes"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}