theonedev/onedev · error · ExplicitException

Shell not ready

Error message

Shell not ready

What it means

KubernetesExecutor.openShell opens an interactive shell (kubectl exec) into a pod. The inner logic first tries to find a suitable shell binary in the container; if the container's command inspection fails or no shell can be determined and the fallback path is not reached, it throws this ExplicitException meaning the shell could not be prepared.

Source

Thrown at server-plugin/server-plugin-executor-kubernetes/src/main/java/io/onedev/server/plugin/executor/kubernetes/KubernetesExecutor.java:369

				String containerNameCopy = containerName;
				if (containerNameCopy != null) {
					Commandline kubectl = newKubectl();
					kubectl.addArgs("exec", "-it", POD_NAME, "-c", containerNameCopy,
							"--namespace", getNamespace(jobContext), "--");

					String shell = null;
					if (containerNameCopy.startsWith("step-")) {
						List<Integer> stepPosition = parseStepPosition(containerNameCopy.substring("step-".length()));
						LeafFacade step = Preconditions.checkNotNull(jobContext.getStep(stepPosition));
						if (step instanceof CommandFacade)
							shell = ((CommandFacade)step).getExecutable();
					}
					if (shell == null) 
						shell = "sh";
					kubectl.addArgs(shell);
					return new CommandlineShell(terminal, kubectl);
				} else {
					throw new ExplicitException("Shell not ready");
				}
			}
		}));
	}
	
	private JobService getJobService() {
		return OneDev.getInstance(JobService.class);
	}
	
	private String getNamespace(JobContext jobContext) {
		return getName() + "-" + jobContext.getProjectId() + "-" 
				+ jobContext.getBuildNumber() + "-" + jobContext.getSubmitSequence();
	}

	private String getTestNamespace() {
		return getName() + "-executor-test";
	}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Install a shell (sh/bash) in the container image, e.g. use a base image that includes busybox or bash
  2. If building the image is not possible, run a debug sidecar/ephemeral container that has a shell
  3. Ensure the pod/container is in Running state before opening a shell
  4. Verify the executor's shell detection logic covers your image's shell path

Example fix

// before (Dockerfile)
FROM gcr.io/distroless/java
// after
FROM gcr.io/distroless/java:debug  # includes busybox shell
// or add a shell in a regular image:
RUN apt-get update && apt-get install -y busybox
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the target container image contains a shell before opening one
// e.g. verify with: kubectl exec <pod> -- sh -c 'command -v sh || command -v bash'
boolean containerHasShell = execInPod(pod, "command -v sh || command -v bash") != null;
if (!containerHasShell) throw new IllegalStateException("No shell in container; use a debug image");

Try / catch

try (ShellSession session = executor.openShell(pod)) { ... } catch (ExplicitException e) { logger.warn("Shell unavailable: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Calling openShell against a pod whose container does not have a usable shell (or the shell detection path fails) so the code reaches the else branch with shell still null and no 'sh' fallback applied.

Common situations: Distroless or minimal container images (scratch, distroless) that ship no shell at all; images where sh/bash are in non-standard locations; pod not fully running when the shell is opened.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/a1a94e8fad188ede. Report an issue: GitHub.