Tencent/tinker · error · IllegalStateException

No odex file was generated after calling performDexOptSecond

Error message

No odex file was generated after calling performDexOptSecondary

What it means

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.

Source

Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java:297

        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);
            if (SharePatchFileUtil.isLegalFile(oatFile)) {
                return true;
            }

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Enable Tinker's interpret mode (useInterpretMode / tinker.enableDexInterpreter or useInterpretModeOnSupported32BitSystem in TinkerApplication) so patched dex runs interpreted without requiring odex.
  2. Free up storage on the device and retry loading the patch; an invalid odex is often caused by failed writes.
  3. Update Tinker to the latest version, which contains device-specific workarounds for performDexOptSecondary failures.
  4. If reproducible on one ROM, capture 'pm dexopt' / dex2oat logs (adb logcat -s dex2oat) and report the device model to Tinker maintainers.

Example fix

// before
TinkerLoadInstall... default dex opt path relies on performDexOptSecondary

// after: gradle config
tinker {
    enableDexInterpreter = true // fall back to interpret mode, no odex required
}
Defensive patterns

Strategy: fallback

Validate before calling

// Before applying patch: check storage and prefer interpret mode on risky ROMs
if (context.getCacheDir().getUsableSpace() < 50L * 1024 * 1024) {
    // defer patch load; low storage breaks dexopt
}
// gradle: tinker { enableDexInterpreter = true }

Try / catch

try {
    TinkerDexOptimizer.optimizeDex(...);
} catch (IllegalStateException e) {
    // odex missing: fall back to interpret-only loading instead of failing patch load
    loadDexInInterpretMode();
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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