testcontainers/testcontainers-java · error · ShellCommandException

Exception when executing

Error message

Exception when executing ${joinedCommand}

What it means

CommandLine.runShellCommand executes an external command via ProcessExecutor and returns its UTF-8 stdout. When the command fails to run or complete — non-zero exit (InvalidExitValueException), timeout, I/O error, or interruption — it wraps the cause in ShellCommandException including the joined command line. It signals that an external helper binary could not be executed successfully.

Solutions

  1. Run the joined command from the message manually in a shell to reproduce and see the real failure.
  2. Install the missing binary or fix PATH so the command resolves.
  3. Fix the command so it exits 0 (or check why Testcontainers calls it); ensure the environment is non-interactive.
  4. Check for JVM shutdown/thread interruption if the cause is InterruptedException.
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the external command exists before Testcontainers calls it
Process which = new ProcessBuilder("which", "<tool>").start();
if (which.waitFor() != 0) throw new IllegalStateException("<tool> not on PATH");

Try / catch

try {
    String out = CommandLine.runShellCommand(cmd...);
} catch (ShellCommandException e) {
    log.error("External command failed: {} cause: {}", e.getMessage(), e.getCause());
    // fall back or rethrow with environment guidance
}

Prevention

When it happens

Trigger: Any Testcontainers path invoking a local command (e.g. docker-machine/other CLI probes) where the binary is missing, exits non-zero, hangs past the timeout, or the JVM thread is interrupted.

Common situations: Required CLI not installed or not on PATH; script with non-zero exit; slow machine hitting the process timeout; command prompting for input; shutdown interrupts mid-execution.

Related errors


AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12). Data as JSON: /api/errors/21dda718a06e4fc9. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/testcontainers/utility/CommandLine.java:40

    private static final Logger LOGGER = LoggerFactory.getLogger(CommandLine.class);

    /**
     * Run a shell command synchronously.
     *
     * @param command command to run and arguments
     * @return the stdout output of the command
     */
    public static String runShellCommand(String... command) {
        String joinedCommand = String.join(" ", command);
        LOGGER.debug("Executing shell command: `{}`", joinedCommand);

        try {
            ProcessResult result = new ProcessExecutor().command(command).readOutput(true).exitValueNormal().execute();

            return result.outputUTF8().trim();
        } catch (IOException | InterruptedException | TimeoutException | InvalidExitValueException e) {
            throw new ShellCommandException("Exception when executing " + joinedCommand, e);
        }
    }

    /**
     * Check whether an executable exists, either at a specific path (if a full path is given) or
     * on the PATH.
     *
     * @param executable the name of an executable on the PATH or a complete path to an executable that may/may not exist
     * @return  whether the executable exists and is executable
     */
    public static boolean executableExists(String executable) {
        // First check if we've been given the full path already
        File directFile = new File(executable);
        if (directFile.exists() && directFile.canExecute()) {
            return true;
        }

        for (String pathString : getSystemPath()) {

View on GitHub (pinned to 8e549514e3)