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
- Install a shell (sh/bash) in the container image, e.g. use a base image that includes busybox or bash
- If building the image is not possible, run a debug sidecar/ephemeral container that has a shell
- Ensure the pod/container is in Running state before opening a shell
- 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
- Use container images that include a shell (add busybox if minimal)
- Test kubectl exec into the pod manually before automating
- Wait for pod Running/Ready state before opening shells
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
- Failed to create ${kind} '${name}': resource still exists af
- Job shell not ready
- Unexpected end of pod watching
- No applicable provisioner discovered for current workspace.
- Component is not a container and so does not contain the pat
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/a1a94e8fad188ede.
Report an issue: GitHub.