HMCL-dev/HMCL · error · IOException

Bad exit code

Error message

Bad exit code: {exitCode}

What it means

SystemUtils.run runs an external process asynchronously with a timeout and throws IOException 'Bad exit code: <n>' when the process finishes with a nonzero exit value. It indicates the command executed but reported failure.

Solutions

  1. Capture and inspect the process stdout/stderr returned by the failed run to see the underlying error.
  2. Run the same command manually in a shell to reproduce and diagnose the nonzero exit.
  3. Fix the command arguments or environment (PATH, permissions) before retrying.
  4. Catch IOException and branch on the exit code if nonzero results are expected/handled.

Example fix

// before
SystemUtils.run(Arrays.asList("git", "statusx"), dir, Duration.ofSeconds(30));
// after
SystemUtils.run(Arrays.asList("git", "status"), dir, Duration.ofSeconds(30));
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the external tool exists and arguments are correct first
if (!new File("/usr/bin/git").canExecute()) throw new IllegalStateException("git not installed");

Try / catch

try {
    result = SystemUtils.run(command, dir, timeout);
} catch (IOException e) {
    if (e.getMessage().startsWith("Bad exit code:")) {
        int code = Integer.parseInt(e.getMessage().substring("Bad exit code: ".length()));
        // inspect captured output, handle by code
    } else throw e;
}

Prevention

When it happens

Trigger: Calling SystemUtils.run(...) with a command that exits nonzero (bad arguments, script failure) within the max wait time.

Common situations: Invoking external tools (git, java, system utilities) that are misconfigured or missing required arguments; scripts failing due to environment differences; underlying program returning failure codes like 1 or 2.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/a80652bc70029ea2. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/SystemUtils.java:107

    public static <T> T run(List<String> command, ExceptionalFunction<InputStream, T, ?> convert) throws Exception {
        return run(command, convert, DEFAULT_MAX_WAIT_TIME);
    }

    public static <T> T run(List<String> command, ExceptionalFunction<InputStream, T, ?> convert, Duration maxWaitTime) throws Exception {
        Process process = new ProcessBuilder(command)
                .redirectError(ProcessBuilder.Redirect.DISCARD)
                .start();
        try {
            InputStream inputStream = process.getInputStream();
            CompletableFuture<T> future = CompletableFuture.supplyAsync(
                    Lang.wrap(() -> convert.apply(inputStream)),
                    Schedulers.io());

            if (!process.waitFor(maxWaitTime.toMillis(), TimeUnit.MILLISECONDS))
                throw new TimeoutException();

            if (process.exitValue() != 0)
                throw new IOException("Bad exit code: " + process.exitValue());

            return future.get();
        } finally {
            if (process.isAlive())
                process.destroy();
        }
    }

    public static boolean supportJVMAttachment() {
        return Thread.currentThread().getContextClassLoader().getResource("com/sun/tools/attach/VirtualMachine.class") != null;
    }

    public static void onLogLine(String log) {
        LOG.info(log);
    }
}

View on GitHub (pinned to 24702dc5a0)