{"record":{"id":"c930080491a2825c","repo":"derailed/k9s","slug":"unable-to-launch-shell-pod-on-node-s","errorCode":null,"errorMessage":"unable to launch shell pod on node %s","messagePattern":"unable to launch shell pod on node (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/view/exec.go","lineNumber":467,"sourceCode":"\t\tif err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tslog.Debug(\"Checking k9s shell pod retries\",\n\t\t\tslogs.Retry, i,\n\t\t\tslogs.PodPhase, pod.Status.Phase,\n\t\t)\n\t\tif pod.Status.Phase == v1.PodRunning {\n\t\t\treturn nil\n\t\t}\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn ctx.Err()\n\t\tcase <-time.After(k9sShellRetryDelay):\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"unable to launch shell pod on node %s\", node)\n}\n\nfunc k9sShellPodName() string {\n\treturn fmt.Sprintf(\"%s-%d\", k9sShell, os.Getpid())\n}\n\nfunc k9sShellPod(node string, cfg *config.ShellPod) *v1.Pod {\n\tvar grace int64\n\tvar priv = true\n\n\tslog.Debug(\"Shell pod config\", slogs.ShellPodCfg, cfg)\n\tc := v1.Container{\n\t\tName:            k9sShell,\n\t\tImage:           cfg.Image,\n\t\tImagePullPolicy: cfg.ImagePullPolicy,\n\t\tVolumeMounts: []v1.VolumeMount{\n\t\t\t{\n\t\t\t\tName:      \"root-vol\",","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/view/exec.go#L449-L485","documentation":"Node-shell flow: k9s creates a privileged helper pod (k9s-<pid>) on the target node and polls its Status.Phase in a retry loop with k9sShellRetryDelay until Running or ctx.Done(). This error means the pod never reached Running within the wait window — the pod is Pending/ImagePullBackOff/CrashLoopOff, or the context/timeout expired first.","triggerScenarios":"Pressing the node shell key with featureGates.nodeShell enabled when the shell pod image cannot be pulled (air-gapped registry), the node is unschedulable/cordoned/full (Pending), RBAC blocks pod creation in kube-system, or the launch is slow enough to blow the context timeout.","commonSituations":"Air-gapped clusters that cannot pull the default shell image; custom shellPod image misconfigured in k9s config; nodes with taints tolerating nothing; strict PodSecurity/Admission blocking the privileged pod.","solutions":["Check the helper pod directly: kubectl get pod k9s-<pid> -n kube-system and kubectl describe it — events show ImagePullBackOff, Unschedulable, or admission denial","Set an image reachable from the cluster in k9s config: shellPod.image (e.g. an internal registry mirror)","Verify the node is schedulable (kubectl uncordon <node>) and has capacity","Grant the user pod-create permissions in kube-system if RBAC is the blocker"],"exampleFix":"# before (k9s config.yaml — default image unreachable in air-gapped cluster)\nk9s:\n  shellPod: {}\n# after — point at an in-cluster reachable image\nk9s:\n  shellPod:\n    image: registry.internal/mirror/alpine:3.20\n    command: [\"/bin/bash\"]\n    namespace: kube-system","handlingStrategy":"retry","validationCode":"// preflight: confirm the shell image is pullable and node schedulable before shelling\nif err := kubectl.CanPull(ctx, cfg.Image); err != nil {\n    return fmt.Errorf(\"shell image %s not pullable: %w\", cfg.Image, err)\n}\nif nodeCordoned(node) {\n    return fmt.Errorf(\"node %s is cordoned\", node)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set shellPod.image to a registry reachable from every cluster you manage","Check kubectl get pod k9s-<pid> -n kube-system events on first failure — it names the real cause","Uncordon nodes and ensure capacity before using node shell","Retry once after slow image pulls; first pull can exceed the wait window"],"tags":["kubernetes","k9s","node","shell-pod","scheduling","image-pull"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}