{"record":{"id":"5efc768cf63db8b8","repo":"Tencent/tinker","slug":"no-odex-file-was-generated-after-calling-performde","errorCode":null,"errorMessage":"No odex file was generated after calling performDexOptSecondary","messagePattern":"No odex file was generated after calling performDexOptSecondary","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java","lineNumber":297,"sourceCode":"\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);\n            if (SharePatchFileUtil.isLegalFile(oatFile)) {\n                return true;\n            }","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java#L279-L315","documentation":"Tinker optimizes patched secondary dex files by invoking PackageManager.performDexOptSecondary and then polls (with retries and 3s sleeps) until the expected .odex/.art file becomes a legal, non-empty file. This IllegalStateException means the system claimed the dexopt API was called but the oat output file never appeared or stayed invalid after all polling attempts. In practice the system dexopt service silently declined to compile the dex.","triggerScenarios":"Calling TinkerDexOptimizer.optimizeDex with useInterpretMode=false (or the checkDexOpt path) on a device where performDexOptSecondary returns success but compilation is skipped: low storage, background dexopt disabled, unsupported ISA/ART version, compile filter 'verify'/'speed-profile' with no profile data, or OEM ROMs that no-op the dexopt request.","commonSituations":"Android 8+ devices with profile-guided compilation and empty profiles; devices with /data nearly full; heavily skinned OEM ROMs (Huawei, Xiaomi) that restrict background dexopt; emulators where dex2oat behaves differently.","solutions":["Enable Tinker's interpret mode (useInterpretMode / tinker.enableDexInterpreter or useInterpretModeOnSupported32BitSystem in TinkerApplication) so patched dex runs interpreted without requiring odex.","Free up storage on the device and retry loading the patch; an invalid odex is often caused by failed writes.","Update Tinker to the latest version, which contains device-specific workarounds for performDexOptSecondary failures.","If reproducible on one ROM, capture 'pm dexopt' / dex2oat logs (adb logcat -s dex2oat) and report the device model to Tinker maintainers."],"exampleFix":"// before\nTinkerLoadInstall... default dex opt path relies on performDexOptSecondary\n\n// after: gradle config\ntinker {\n    enableDexInterpreter = true // fall back to interpret mode, no odex required\n}","handlingStrategy":"fallback","validationCode":"// Before applying patch: check storage and prefer interpret mode on risky ROMs\nif (context.getCacheDir().getUsableSpace() < 50L * 1024 * 1024) {\n    // defer patch load; low storage breaks dexopt\n}\n// gradle: tinker { enableDexInterpreter = true }","typeGuard":null,"tryCatchPattern":"try {\n    TinkerDexOptimizer.optimizeDex(...);\n} catch (IllegalStateException e) {\n    // odex missing: fall back to interpret-only loading instead of failing patch load\n    loadDexInInterpretMode();\n}","preventionTips":["Enable interpret mode for patched dex so success never depends on system dexopt.","Keep at least ~50MB free storage expectation before triggering patch load.","Track device models where odex generation fails and target interpret mode via server-side patch config."],"tags":["android","dexopt","art","odex","tinker","hotfix"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}