MuntashirAkon/AppManager · error · IOException

ODEX isn't supported.

Error message

ODEX isn't supported.

What it means

This DexClasses constructor builds a class-name map from a dex/odex file via dexlib2. ODEX files use optimized opcodes whose semantics differ from standard dex; the code rejects them before disassembly unless an Odex-specific resolver is configured — but supportsOptimizedOpcodes() on the main dex triggers an explicit IOException('ODEX isn't supported.') because the rest of the pipeline (smali/baksmali handling here) expects plain dex. It exists to fail fast instead of producing wrong decompiled output.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/dex/DexClasses.java:78

            DexBackedDexFile dexFile = dexEntry.getDexFile();
            // Store list of classes
            for (ClassDef classDef : dexFile.getClasses()) {
                String name = formatter.getType(classDef.getType());
                if (name.endsWith(";")) name = name.substring(0, name.length() - 1);
                if (name.startsWith("L")) {
                    name = name.substring(1).replace('/', '.');
                }
                mClassNameClassDefMap.put(name, classDef);
                String baseClass = DexUtils.getClassNameWithoutInnerClasses(name);
                List<String> classes = mBaseClassNestedClassMap.get(baseClass);
                if (classes == null) {
                    classes = new ArrayList<>();
                    mBaseClassNestedClassMap.put(baseClass, classes);
                }
                classes.add(name);
            }
            if (dexFile.supportsOptimizedOpcodes()) {
                throw new IOException("ODEX isn't supported.");
            }
            if (dexFile instanceof DexBackedOdexFile) {
                mOptions.inlineResolver = InlineMethodResolver.createInlineMethodResolver(
                        ((DexBackedOdexFile) dexFile).getOdexVersion());
            }
        }
    }

    public DexClasses(@NonNull InputStream inputStream, @IntRange(from = -1) int apiLevel) throws IOException {
        mOpcodes = apiLevel < 0 ? Opcodes.getDefault() : Opcodes.forApi(apiLevel);
        mOptions = new BaksmaliOptions();
        // options
        mOptions.deodex = false;
        mOptions.implicitReferences = false;
        mOptions.parameterRegisters = true;
        mOptions.localsDirective = true;
        mOptions.sequentialLabels = true;
        mOptions.debugInfo = BuildConfig.DEBUG;

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Supply the original classes.dex from the APK instead of the device-compiled odex
  2. Deoptimize first: pull the APK from the server, or use a tool that converts odex to dex (e.g. baksmali's odex support / vdex-odex extractors)
  3. If you truly need odex handling, use a code path that sets up DexBackedOdexFile with InlineMethodResolver instead of this constructor
  4. Skip odex-only entries when iterating app artifacts and log them as unsupported

Example fix

// before
new DexClasses(odexPath, options); // IOException: ODEX isn't supported
// after
File apk = new File("/data/app/com.example/base.apk");
if (hasPlainDex(apk)) {
    new DexClasses(apk, options);
} else {
    throw new UnsupportedOperationException("App is odex-only; deoptimize or pull APK from server");
}
Defensive patterns

Strategy: validation

Validate before calling

DexBackedDexFile dexFile = DexBackedDexFile.fromInputStream(options, new BufferedInputStream(in));
boolean isOdex = dexFile.supportsOptimizedOpcodes();
if (isOdex) throw new UnsupportedOperationException("Supply a plain classes.dex, not an odex");

Type guard

boolean isPlainDex(DexBackedDexFile f) { return !f.supportsOptimizedOpcodes(); }

Try / catch

try {
    new DexClasses(path, options);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).contains("ODEX")) {
        Log.w(TAG, "odex not supported; pull the APK from server or deoptimize first");
    }
}

Prevention

When it happens

Trigger: Constructing DexClasses over a DexFile whose supportsOptimizedOpcodes() returns true — i.e. an odex file (or a dex that claims optimized opcodes) was supplied where a plain classes.dex was expected: pointing the loader at an odex (e.g. system app's .odex, framework .art/odex extract), or loading an extracted dex that was pre-optimized.

Common situations: Trying to decompile a system app whose code is compiled as odex on-device (no classes.dex present); analyzing a vendor ROM with heavy ART pre-optimization; passing the wrong file from an APK's extracted artifacts.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/cba47aa557a9362d. Report an issue: GitHub.