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.
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 ' ': resource still exists after deletion
- Component is not a container and so does not contain the…
- Job shell not ready
- No applicable provisioner discovered for current workspace…
- OneDev running inside kubernetes cluster does not support…
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/a1a94e8fad188ede.
Report an issue: GitHub.
Appendix: 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)