{"record":{"id":"e64191421f2269f8","repo":"github/copilot-sdk","slug":"cli-process-exited-unexpectedly","errorCode":null,"errorMessage":"CLI process exited unexpectedly.","messagePattern":"CLI process exited unexpectedly\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"java/sdk/src/main/java/com/github/copilot/CliServerManager.java","lineNumber":199,"sourceCode":"            }\n        }, \"cli-stderr-reader\");\n        thread.setDaemon(true);\n        thread.start();\n        this.stderrThread = thread;\n    }\n\n    private Integer waitForPortAnnouncement(Process process) throws IOException {\n        try (BufferedReader reader = new BufferedReader(\n                new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {\n            Pattern portPattern = Pattern.compile(\"listening on port (\\\\d+)\", Pattern.CASE_INSENSITIVE);\n            long deadline = System.currentTimeMillis() + 30000;\n\n            while (System.currentTimeMillis() < deadline) {\n                String line = reader.readLine();\n                if (line == null) {\n                    awaitStderrReader();\n                    String stderr = getStderrOutput();\n                    throw new IOException(formatCliExitedMessage(\"CLI process exited unexpectedly.\", stderr));\n                }\n\n                Matcher matcher = portPattern.matcher(line);\n                if (matcher.find()) {\n                    return Integer.parseInt(matcher.group(1));\n                }\n            }\n\n            process.destroyForcibly();\n            throw new IOException(\"Timeout waiting for CLI to announce port\");\n        }\n    }\n\n    String getStderrOutput() {\n        synchronized (stderrBuffer) {\n            return stderrBuffer.toString().trim();\n        }\n    }","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/CliServerManager.java#L181-L217","documentation":"When starting the CLI in stdio mode, waitForPortAnnouncement reads stdout lines waiting for the CLI to announce its TCP port. If stdout closes (readLine returns null) before a port line is seen, the CLI process has terminated, so the manager throws IOException (augmented with captured stderr via formatCliExitedMessage).","triggerScenarios":"startCliServer spawns the CLI process, but the process exits during startup — bad CLI path, missing executable permissions, corrupted install, crashing CLI, or fatal startup error printed to stderr.","commonSituations":"Wrong cliPath pointing to a non-CLI binary; CLI crashes on unsupported OS/arch or missing dependencies; stderr shows auth or config fatal errors; CLI killed by the environment (OOM, sandbox).","solutions":["Catch the IOException and read the attached stderr output to find the CLI's actual failure reason.","Verify cliPath points to a working, executable CLI binary (`<path> --version`).","Reinstall/update the Copilot CLI to a compatible version.","Check environment constraints (sandbox, permissions, architecture) that could kill the process."],"exampleFix":"// before\nJsonRpcClient client = manager.startCliServer();\n// after\ntry {\n    JsonRpcClient client = manager.startCliServer();\n} catch (IOException e) {\n    log.error(\"CLI failed to start: {}\", e.getMessage()); // includes stderr\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"ProcessBuilder pb = new ProcessBuilder(cliPath, \"--version\");\npb.start().waitFor(10, TimeUnit.SECONDS); // fail fast if binary is broken","typeGuard":null,"tryCatchPattern":"try {\n  client = manager.startCliServer();\n} catch (IOException e) {\n  // e.getMessage() includes CLI stderr — surface it to the user\n  throw new StartupException(\"CLI failed: \" + e.getMessage(), e);\n}","preventionTips":["Verify cliPath with `--version` before starting the server.","Inspect stderr output on startup failure for the root cause.","Keep the CLI installed, executable, and architecturally compatible.","Wrap startup in retry with backoff for transient crashes."],"tags":["java","cli","process","startup-failure"],"backgroundTag":"command-not-found","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}