{"record":{"id":"eed02596ef652324","repo":"Tencent/tinker","slug":"no-odex-file-was-generated-after-calling-registerd","errorCode":null,"errorMessage":"No odex file was generated after calling registerDexModule","messagePattern":"No odex file was generated after calling registerDexModule","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java","lineNumber":294,"sourceCode":"                return;\n            }\n        }\n\n        if (!SharePatchFileUtil.isLegalFile(oatFile)) {\n            if (\"huawei\".equalsIgnoreCase(Build.MANUFACTURER) || \"honor\".equalsIgnoreCase(Build.MANUFACTURER)) {\n                for (int i = 0; i < 5; ++i) {\n                    try {\n                        registerDexModule(context, dexPath);\n                        if (SharePatchFileUtil.isLegalFile(oatFile)) {\n                            break;\n                        }\n                    } catch (Throwable thr) {\n                        ShareTinkerLog.printErrStackTrace(TAG, thr, \"[-] Error.\");\n                    }\n                    SystemClock.sleep(3000);\n                }\n                if (!SharePatchFileUtil.isLegalFile(oatFile)) {\n                    throw new IllegalStateException(\"No odex file was generated after calling registerDexModule\");\n                }\n            } else {\n                throw new IllegalStateException(\"No odex file was generated after calling performDexOptSecondary\");\n            }\n        }\n    }\n\n    private static boolean triggerSecondaryDexOpt(Context context, File dexFile, File oatFile, boolean waitForOAT) {\n        try {\n            performDexOptSecondary(context);\n            if (SharePatchFileUtil.isLegalFile(oatFile)) {\n                return true;\n            }\n        } catch (Throwable thr) {\n            ShareTinkerLog.printErrStackTrace(TAG, thr, \"[-] Error.\");\n        }\n        try {\n            performBgDexOptJob(context);","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java#L276-L312","documentation":"On the dex-optimization fallback path (devices where performDexOptSecondary did not produce a usable odex), TinkerDexOptimizer calls registerDexModule (the Rebel/DexOptimizer API) up to 5 times, sleeping 3s between attempts and checking SharePatchFileUtil.isLegalFile(oatFile) each round. If after 5 attempts the odex is still absent/empty it throws IllegalStateException('No odex file was generated after calling registerDexModule'). The system repeatedly declined to compile the patch dex.","triggerScenarios":"registerDexModule silently no-op'ing (listener never reporting success, throwing and being logged as '[-] Error.'), or dex2oat failing asynchronously so the oat file is never written/complete within the ~15s retry window; also oat files written then deleted by storage management.","commonSituations":"Vendor ROMs (vivo/oppo per tinker's own comments) that bypass or throttle dex2oat for non-Play channels; low-storage or battery-saver modes deferring compilation; devices under heavy load where dex2oat times out; Android versions where registerDexModule is restricted.","solutions":["Retry patch load on next app launch — the odex may succeed once the device is idle; ensure your patch-load flow tolerates a failed optimize attempt.","Free storage and avoid loading patches during first-boot/battery-saver conditions; schedule the patch load when the device is charging/idle.","If it persists on a specific ROM, skip the strict odex requirement: tinker's own code notes it is fine to accept interpret-mode loading on such devices (the check can be relaxed via the documented special-device path).","Upgrade tinker — the retry/verification strategy around registerDexModule has been improved across releases."],"exampleFix":"// before\nboolean ok = TinkerDexOptimizer.optimizeAll(context, dexFiles, oatDir, listener); // eventually throws IllegalStateException\n\n// after\ntry {\n    TinkerDexOptimizer.optimizeAll(context, dexFiles, oatDir, listener);\n} catch (IllegalStateException e) {\n    ShareTinkerLog.w(TAG, \"dex2oat unavailable on this device, proceed with interpret-mode loading\", e);\n    // continue patch load without odex; class loading falls back to interpretation\n}","handlingStrategy":"retry","validationCode":"if (!SharePatchFileUtil.isLegalFile(oatFile) && isKnownBypassVendor(Build.MANUFACTURER)) {\n    // skip strict odex requirement; load dex in interpret mode\n}","typeGuard":null,"tryCatchPattern":"catch IllegalStateException 'No odex file was generated after calling registerDexModule' -> continue loading the patch without odex (interpret mode) or retry on next launch","preventionTips":["Treat odex generation as best-effort: the dex still loads in interpret mode without it.","Load patches while the device is idle/charging so dex2oat is not deferred.","Maintain a vendor blacklist for ROMs that bypass dex2oat and relax the check there."],"tags":["dex2oat","odex","registerdexmodule","vendor-rom","retry"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}