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

  1. 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.
  2. Free storage and avoid loading patches during first-boot/battery-saver conditions; schedule the patch load when the device is charging/idle.
  3. 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).
  4. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/eed02596ef652324. Report an issue: GitHub.