{"record":{"id":"26c06872776820f3","repo":"theonedev/onedev","slug":"job-shell-not-ready","errorCode":null,"errorMessage":"Job shell not ready","messagePattern":"Job shell not ready","errorType":"exception","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/job/DefaultJobService.java","lineNumber":953,"sourceCode":"\t\t}\n\t}\n\n\t@Sessional\n\t@Override\n\tpublic JobShell openShell(Build build, JobTerminal terminal) {\n\t\tvar jobContext = getJobContext(build.getId());\n\t\tif (jobContext != null) {\n\t\t\tvar jobToken = jobContext.getJobToken();\n\t\t\tvar shellServer = jobServers.get(jobToken);\n\t\t\tif (shellServer != null) {\n\t\t\t\tclusterService.runOnServer(shellServer, () -> {\n\t\t\t\t\tvar innerJobContext = getJobContext(jobToken, true);\n\t\t\t\t\tvar jobRunnable = jobRunnables.get(innerJobContext.getJobToken());\n\t\t\t\t\tif (jobRunnable != null) {\n\t\t\t\t\t\tvar shell = jobRunnable.openShell(innerJobContext, terminal);\n\t\t\t\t\t\tjobShells.put(terminal.getSessionId(), shell);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ExplicitException(\"Job shell not ready\");\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t});\n\n\t\t\t\treturn new JobShell(build.getId(), terminal.getSessionId()) {\n\n\t\t\t\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void writeToStdin(String data) {\n\t\t\t\t\t\tclusterService.runOnServer(shellServer, () -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tvar shell = jobShells.get(terminal.getSessionId());\n\t\t\t\t\t\t\t\tif (shell != null)\n\t\t\t\t\t\t\t\t\tshell.writeToStdin(data);\n\t\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t\t\tlogException(\"Error sending shell input\", e);\n\t\t\t\t\t\t\t}","sourceCodeStart":935,"sourceCodeEnd":971,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/job/DefaultJobService.java#L935-L971","documentation":"Thrown by DefaultJobService.openShell when a terminal session is opened for a running build, but on the cluster server that owns the job server, no JobRunnable is registered for the job token. It means the build's executing process is not (yet or anymore) able to host an interactive shell. The library throws it as an ExplicitException so the UI shows the message directly.","triggerScenarios":"Calling openShell(build, terminal) (e.g. user clicks the terminal button on a build) when jobRunnables.get(jobToken) returns null on the shell server — typically in the window between job server registration and job runnable start, or after the job executor process died.","commonSituations":"Clicking 'Open terminal' right as the build starts or finishes; the job runner container crashed; build is queued/pending but a stale JobContext still exists; multi-node cluster race where the shell server differs from the running node.","solutions":["Wait until the build reaches a running state with an active step, then retry opening the terminal","Refresh the build page and reopen the terminal (stale JobContext/jobServer entries may be gone)","Check the job executor/logs to confirm the job process is actually alive","If using a cluster, verify the node hosting the job is healthy"],"exampleFix":"// before: opening shell immediately after clicking build\nvar shell = jobService.openShell(build, terminal);\n// after: only open when build is running\nif (build.getStatus() == BuildStatus.RUNNING)\n    var shell = jobService.openShell(build, terminal);\nelse\n    throw new IllegalStateException(\"Build is not running; terminal unavailable\");","handlingStrategy":"retry","validationCode":"// only attempt terminal when build is actively running\nif (build.getStatus() != BuildStatus.RUNNING)\n    throw new IllegalStateException(\"Terminal requires a running build\");","typeGuard":"boolean shellAvailable = build.getStatus() == BuildStatus.RUNNING;","tryCatchPattern":"try {\n    var shell = jobService.openShell(build, terminal);\n} catch (ExplicitException e) {\n    // show 'terminal not ready, retry shortly' message\n}","preventionTips":["Only surface the terminal button for RUNNING builds","Retry after a short delay for builds that just started","Monitor executor health in clustered setups"],"tags":["ci","job-execution","shell","race-condition"],"backgroundTag":"resource-not-found","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}