{"record":{"id":"cba47aa557a9362d","repo":"MuntashirAkon/AppManager","slug":"odex-isn-t-supported","errorCode":null,"errorMessage":"ODEX isn't supported.","messagePattern":"ODEX isn't supported\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/dex/DexClasses.java","lineNumber":78,"sourceCode":"            DexBackedDexFile dexFile = dexEntry.getDexFile();\n            // Store list of classes\n            for (ClassDef classDef : dexFile.getClasses()) {\n                String name = formatter.getType(classDef.getType());\n                if (name.endsWith(\";\")) name = name.substring(0, name.length() - 1);\n                if (name.startsWith(\"L\")) {\n                    name = name.substring(1).replace('/', '.');\n                }\n                mClassNameClassDefMap.put(name, classDef);\n                String baseClass = DexUtils.getClassNameWithoutInnerClasses(name);\n                List<String> classes = mBaseClassNestedClassMap.get(baseClass);\n                if (classes == null) {\n                    classes = new ArrayList<>();\n                    mBaseClassNestedClassMap.put(baseClass, classes);\n                }\n                classes.add(name);\n            }\n            if (dexFile.supportsOptimizedOpcodes()) {\n                throw new IOException(\"ODEX isn't supported.\");\n            }\n            if (dexFile instanceof DexBackedOdexFile) {\n                mOptions.inlineResolver = InlineMethodResolver.createInlineMethodResolver(\n                        ((DexBackedOdexFile) dexFile).getOdexVersion());\n            }\n        }\n    }\n\n    public DexClasses(@NonNull InputStream inputStream, @IntRange(from = -1) int apiLevel) throws IOException {\n        mOpcodes = apiLevel < 0 ? Opcodes.getDefault() : Opcodes.forApi(apiLevel);\n        mOptions = new BaksmaliOptions();\n        // options\n        mOptions.deodex = false;\n        mOptions.implicitReferences = false;\n        mOptions.parameterRegisters = true;\n        mOptions.localsDirective = true;\n        mOptions.sequentialLabels = true;\n        mOptions.debugInfo = BuildConfig.DEBUG;","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/dex/DexClasses.java#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Supply the original classes.dex from the APK instead of the device-compiled odex","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)","If you truly need odex handling, use a code path that sets up DexBackedOdexFile with InlineMethodResolver instead of this constructor","Skip odex-only entries when iterating app artifacts and log them as unsupported"],"exampleFix":"// before\nnew DexClasses(odexPath, options); // IOException: ODEX isn't supported\n// after\nFile apk = new File(\"/data/app/com.example/base.apk\");\nif (hasPlainDex(apk)) {\n    new DexClasses(apk, options);\n} else {\n    throw new UnsupportedOperationException(\"App is odex-only; deoptimize or pull APK from server\");\n}","handlingStrategy":"validation","validationCode":"DexBackedDexFile dexFile = DexBackedDexFile.fromInputStream(options, new BufferedInputStream(in));\nboolean isOdex = dexFile.supportsOptimizedOpcodes();\nif (isOdex) throw new UnsupportedOperationException(\"Supply a plain classes.dex, not an odex\");","typeGuard":"boolean isPlainDex(DexBackedDexFile f) { return !f.supportsOptimizedOpcodes(); }","tryCatchPattern":"try {\n    new DexClasses(path, options);\n} catch (IOException e) {\n    if (String.valueOf(e.getMessage()).contains(\"ODEX\")) {\n        Log.w(TAG, \"odex not supported; pull the APK from server or deoptimize first\");\n    }\n}","preventionTips":["Always analyze the original APK's classes.dex, not on-device odex artifacts","Detect odex files by magic ('dey') before loading","Skip or convert odex entries in batch decompilation jobs"],"tags":["dex","odex","android","decompilation"],"backgroundTag":"unsupported-operation","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}