iBotPeaches/Apktool · warning · BrutException

could not exec :

Error message

could not exec : 

What it means

Thrown by OS.exec when ps.waitFor() is interrupted — i.e. the waiting thread got an interrupt (shutdown hook, timeout logic, thread pool cancellation) while the child process ran. Note the variant message with an extra space ('could not exec : ') and that the code does not re-interrupt the thread or destroy the child process, so the orphaned process may keep running.

Source

Thrown at brut.j.util/src/main/java/brut/util/OS.java:152

        }
    }

    public static void exec(String[] cmd) throws BrutException {
        try {
            ProcessBuilder builder = new ProcessBuilder(cmd);
            Process ps = builder.start();

            new StreamForwarder(ps.getErrorStream(), "ERROR").start();
            new StreamForwarder(ps.getInputStream(), "OUTPUT").start();

            int exitValue = ps.waitFor();
            if (exitValue != 0) {
                throw new BrutException("Execution failed (exit code = " + exitValue + "): " + Arrays.toString(cmd));
            }
        } catch (IOException ex) {
            throw new BrutException("could not exec: " + Arrays.toString(cmd), ex);
        } catch (InterruptedException ex) {
            throw new BrutException("could not exec : " + Arrays.toString(cmd), ex);
        }
    }

    public static String execAndReturn(String[] cmd) {
        ExecutorService executor = Executors.newCachedThreadPool();
        try {
            ProcessBuilder builder = new ProcessBuilder(cmd);
            builder.redirectErrorStream(true);

            Process process = builder.start();
            StreamCollector collector = new StreamCollector(process.getInputStream());
            executor.execute(collector);
            process.waitFor(15, TimeUnit.SECONDS);
            executor.shutdownNow();

            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                Log.w(TAG, "Stream collector did not terminate.");
            }

View on GitHub (pinned to 79b63384d7)

Solutions

  1. Avoid interrupting threads that run OS.exec; use process-level timeouts (waitFor(t, unit)) or a paged design instead of thread interruption
  2. In the catch block for BrutException, check whether the cause is InterruptedException and if so kill any child process your code tracks and restore the interrupt flag
  3. Give external tools more headroom (bigger timeout, faster inputs) so they finish before watchdogs fire
  4. If you control the caller, use OS.execAndReturn or your own ProcessBuilder wrapper that destroys the process on interrupt

Example fix

// before
Future<?> f = pool.submit(() -> OS.exec(cmd));
f.cancel(true); // interrupts -> "could not exec : ..." and child keeps running

// after
// don't interrupt; bound the child instead
ProcessBuilder b = new ProcessBuilder(cmd);
Process p = b.start();
if (!p.waitFor(5, TimeUnit.MINUTES)) {
    p.destroyForcibly();
    throw new BrutException("tool timed out");
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    OS.exec(cmd);
} catch (BrutException e) {
    if (e.getCause() instanceof InterruptedException) {
        Thread.currentThread().interrupt(); // restore the flag the library swallowed
        // kill any child process you tracked; the library leaves it running
        cleanupChildProcesses();
        throw new CancelledException("tool run interrupted", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: OS.exec(cmd) on a thread that another thread interrupts: executor shutdownNow(), a watchdog cancelling long-running external tools, or JVM shutdown hooks interrupting workers while aapt/zip is still executing.

Common situations: CI pipelines enforcing timeouts that interrupt build threads mid-tool-run; thread pools calling shutdownNow() during cleanup; user cancelling a build in an IDE; long-running external commands outliving their welcome because the interrupt path never destroys the process.

Related errors


AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14). Data as JSON: /api/errors/8a11b7279c8e9078. Report an issue: GitHub.