asLody/VirtualApp · warning · IOException

dex2oat works unsuccessfully, exit code

Error message

dex2oat works unsuccessfully, exit code: ${ret}

What it means

ArtDexOptimizer.interpretDex2Oat shells out to the dex2oat binary to compile the plugin's dex into OAT/odex. If the dex2oat process exits with a non-zero status, the library throws this IOException, meaning the plugin's native compilation failed (though the app may still run in interpreter mode).

Solutions

  1. Check the dex2oat stderr output (consumed by StreamConsumer) by rerunning the dex2oat command manually for the real failure reason
  2. Disable/relax OAT compilation so the plugin runs via interpreter fallback (skip optimize) — the app usually still works
  3. Free disk space in the plugin data/OAT directory and retry
  4. Verify the plugin APK's dex is valid and the device ROM's dex2oat supports it

Example fix

// before
// throws and aborts when dex2oat fails
ArtDexOptimizer.interpretDex2Oat(dexPath, oatPath, ...);
// after
try {
    ArtDexOptimizer.interpretDex2Oat(dexPath, oatPath, ...);
} catch (IOException e) {
    Log.w(TAG, "dex2oat failed, falling back to interpreter mode", e); // continue without OAT
}
Defensive patterns

Strategy: fallback

Validate before calling

File oatDir = new File(oatPath).getParentFile(); if (!oatDir.canWrite() || oatDir.getUsableSpace() < requiredBytes) { skipOptimization(); }

Try / catch

try { ArtDexOptimizer.interpretDex2Oat(dex, oat, ...); } catch (IOException e) { Log.w(TAG, "OAT skipped: " + e.getMessage()); /* proceed in interpreter mode */ }

Prevention

When it happens

Trigger: dex2oat exits non-zero: unsupported dex bytecode, corrupted APK/dex, insufficient disk space in the OAT output directory, incompatible dex2oat version on the device ROM, or invalid compiler flags.

Common situations: Custom ROMs with a broken/missing dex2oat; Android version mismatch between dex flags and the ART version; out-of-space on /data; plugin APK containing desugared/preview bytecode the device dex2oat can't handle.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09). Data as JSON: /api/errors/07cd39ff94397ec3. Report an issue: GitHub.

Appendix: source

Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/helper/ArtDexOptimizer.java:59

        }
        commandAndParams.add("--dex-file=" + dexFilePath);
        commandAndParams.add("--oat-file=" + oatFilePath);
        commandAndParams.add("--instruction-set=" + VMRuntime.getCurrentInstructionSet.call());
        if (Build.VERSION.SDK_INT > 25) {
            commandAndParams.add("--compiler-filter=quicken");
        } else {
            commandAndParams.add("--compiler-filter=interpret-only");
        }

        final ProcessBuilder pb = new ProcessBuilder(commandAndParams);
        pb.redirectErrorStream(true);
        final Process dex2oatProcess = pb.start();
        StreamConsumer.consumeInputStream(dex2oatProcess.getInputStream());
        StreamConsumer.consumeInputStream(dex2oatProcess.getErrorStream());
        try {
            final int ret = dex2oatProcess.waitFor();
            if (ret != 0) {
                throw new IOException("dex2oat works unsuccessfully, exit code: " + ret);
            }
        } catch (InterruptedException e) {
            throw new IOException("dex2oat is interrupted, msg: " + e.getMessage(), e);
        }
    }

    private static class StreamConsumer {
        static final Executor STREAM_CONSUMER = Executors.newSingleThreadExecutor();

        static void consumeInputStream(final InputStream is) {
            STREAM_CONSUMER.execute(new Runnable() {
                @Override
                public void run() {
                    if (is == null) {
                        return;
                    }
                    final byte[] buffer = new byte[256];
                    try {

View on GitHub (pinned to 666fefcb5d)