{"record":{"id":"5d89fbf36b53494c","repo":"iBotPeaches/Apktool","slug":"execution-failed-exit-code","errorCode":null,"errorMessage":"Execution failed (exit code = ","messagePattern":"Execution failed \\(exit code = ","errorType":"exception","errorClass":"BrutException","httpStatus":null,"severity":"error","filePath":"brut.j.util/src/main/java/brut/util/OS.java","lineNumber":147,"sourceCode":"            if (file.isDirectory()) {\n                cpdir(file, destFile);\n            } else {\n                cpfile(file, destFile);\n            }\n        }\n    }\n\n    public static void exec(String[] cmd) throws BrutException {\n        try {\n            ProcessBuilder builder = new ProcessBuilder(cmd);\n            Process ps = builder.start();\n\n            new StreamForwarder(ps.getErrorStream(), \"ERROR\").start();\n            new StreamForwarder(ps.getInputStream(), \"OUTPUT\").start();\n\n            int exitValue = ps.waitFor();\n            if (exitValue != 0) {\n                throw new BrutException(\"Execution failed (exit code = \" + exitValue + \"): \" + Arrays.toString(cmd));\n            }\n        } catch (IOException ex) {\n            throw new BrutException(\"could not exec: \" + Arrays.toString(cmd), ex);\n        } catch (InterruptedException ex) {\n            throw new BrutException(\"could not exec : \" + Arrays.toString(cmd), ex);\n        }\n    }\n\n    public static String execAndReturn(String[] cmd) {\n        ExecutorService executor = Executors.newCachedThreadPool();\n        try {\n            ProcessBuilder builder = new ProcessBuilder(cmd);\n            builder.redirectErrorStream(true);\n\n            Process process = builder.start();\n            StreamCollector collector = new StreamCollector(process.getInputStream());\n            executor.execute(collector);\n            process.waitFor(15, TimeUnit.SECONDS);","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/iBotPeaches/Apktool/blob/79b63384d7d7e22917e6ea8b453272da7012515b/brut.j.util/src/main/java/brut/util/OS.java#L129-L165","documentation":"Thrown by OS.exec after an external process started successfully but returned a non-zero exit code. The message includes the numeric exit code and the full command array. This means the binary was found and ran — it failed on its own (bad arguments, missing input files, version mismatch). The child's stderr was forwarded to the parent's stderr by StreamForwarder, so the underlying tool's message usually appears in the log just before this exception.","triggerScenarios":"OS.exec(new String[]{...}) where the spawned tool (e.g. aapt, apkbuilder, zip, keytool) exits non-zero: invalid flags, missing/locked input file, unsupported format, out-of-memory in the child, or a different tool version with changed CLI syntax.","commonSituations":"aapt rejecting a newer Android resource format than the bundled aapt supports; incompatible platform-tools or JDK version bundled with the library; passing Windows-style paths on Unix; child printing 'file not found' for an input apk; CI environments missing 32-bit binaries or libz.","solutions":["Re-run with the child's stderr visible — StreamForwarder already pipes it to your stderr — and fix the argument or input the child complains about","Verify the exact binary being invoked (which aapt / version flag) matches what your inputs require; upgrade apktool or the bundled tool","Validate input files exist and are readable before invoking the external command","For CI failures, check the child's exit-code contract (e.g. 127 = command not found, 139 = segfault) to narrow env vs. args"],"exampleFix":"// before\nOS.exec(new String[]{aapt, \"p\", \"-f\", \"-M\", manifest, \"-F\", out}); // exit 1\n\n// after\nif (!new File(manifest).isFile()) throw new BrutException(\"missing manifest: \" + manifest);\nOS.exec(new String[]{aapt, \"p\", \"-f\", \"-M\", manifest, \"-F\", out});\n// on failure: child stderr is already forwarded — read it for the real cause","handlingStrategy":"try-catch","validationCode":"boolean inputsReady(String[] cmd, int firstInputArgIdx) {\n    for (int i = firstInputArgIdx; i < cmd.length; i++) {\n        File f = new File(cmd[i]);\n        if (f.getPath().startsWith(\"-\") || i < firstInputArgIdx) continue;\n        if (f.exists() && !f.canRead()) return false;\n    }\n    return new File(cmd[0]).canExecute() || Boolean.TRUE;\n}","typeGuard":null,"tryCatchPattern":"try {\n    OS.exec(cmd);\n} catch (BrutException e) {\n    String m = e.getMessage();\n    if (m.startsWith(\"Execution failed\")) {\n        // child ran and failed: its stderr was forwarded to our stderr — mine it for the cause\n        throw new BuildFailure(\"tool exited non-zero: \" + m, e);\n    }\n    throw e;\n}","preventionTips":["Capture and log the child's stderr (StreamForwarder already prints it) so exit-code failures are diagnosable","Pin tool versions (aapt, platform-tools) matching your input formats","Validate input file existence before invoking external tools"],"tags":["process","exec","external-tool","exit-code","java"],"backgroundTag":null,"analyzedSha":"79b63384d7d7e22917e6ea8b453272da7012515b","analyzedAt":"2026-08-14T10:43:28.812Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}