Tencent/tinker · error · IllegalStateException
No odex file was generated after calling registerDexModule
Error message
No odex file was generated after calling registerDexModule
What it means
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.
Source
Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java:294
return;
}
}
if (!SharePatchFileUtil.isLegalFile(oatFile)) {
if ("huawei".equalsIgnoreCase(Build.MANUFACTURER) || "honor".equalsIgnoreCase(Build.MANUFACTURER)) {
for (int i = 0; i < 5; ++i) {
try {
registerDexModule(context, dexPath);
if (SharePatchFileUtil.isLegalFile(oatFile)) {
break;
}
} catch (Throwable thr) {
ShareTinkerLog.printErrStackTrace(TAG, thr, "[-] Error.");
}
SystemClock.sleep(3000);
}
if (!SharePatchFileUtil.isLegalFile(oatFile)) {
throw new IllegalStateException("No odex file was generated after calling registerDexModule");
}
} else {
throw new IllegalStateException("No odex file was generated after calling performDexOptSecondary");
}
}
}
private static boolean triggerSecondaryDexOpt(Context context, File dexFile, File oatFile, boolean waitForOAT) {
try {
performDexOptSecondary(context);
if (SharePatchFileUtil.isLegalFile(oatFile)) {
return true;
}
} catch (Throwable thr) {
ShareTinkerLog.printErrStackTrace(TAG, thr, "[-] Error.");
}
try {
performBgDexOptJob(context);View on GitHub (pinned to 1b7ea02c23)
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.
Example fix
// before
boolean ok = TinkerDexOptimizer.optimizeAll(context, dexFiles, oatDir, listener); // eventually throws IllegalStateException
// after
try {
TinkerDexOptimizer.optimizeAll(context, dexFiles, oatDir, listener);
} catch (IllegalStateException e) {
ShareTinkerLog.w(TAG, "dex2oat unavailable on this device, proceed with interpret-mode loading", e);
// continue patch load without odex; class loading falls back to interpretation
} Defensive patterns
Strategy: retry
Validate before calling
if (!SharePatchFileUtil.isLegalFile(oatFile) && isKnownBypassVendor(Build.MANUFACTURER)) {
// skip strict odex requirement; load dex in interpret mode
} Try / catch
catch IllegalStateException 'No odex file was generated after calling registerDexModule' -> continue loading the patch without odex (interpret mode) or retry on next launch
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Bad oat magic: %x %x %x %x
- patch dex opt file not found, but path is null!!!!
- Bad oat version: {}
- No odex file was generated after calling performDexOptSecond
- dex2oat works unsuccessfully, exit code:
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/eed02596ef652324.
Report an issue: GitHub.