Tencent/tinker · critical · TinkerRuntimeException

error load patch version file not exist, but file is null

Error message

error load patch version file not exist, but file is null

What it means

Thrown by TinkerLoadResult#load in the ERROR_LOAD_PATCH_VERSION_FILE_NOT_EXIST branch when patchVersionFile is null. The load code already says the version file (patch-xxx.apk info file) is missing; the code then needs that File to report onLoadFileNotFound. A null file means the result Intent lacked the version-file path extra, so the state is internally inconsistent.

Source

Thrown at tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/tinker/TinkerLoadResult.java:180

            case ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED:
                ShareTinkerLog.e(TAG, "path info corrupted");
                tinker.getLoadReporter().onLoadPatchInfoCorrupted(oldVersion, newVersion, patchInfoFile);
                break;

            case ShareConstants.ERROR_LOAD_PATCH_INFO_BLANK:
                ShareTinkerLog.e(TAG, "path info blank, wait main process to restart");
                break;

            case ShareConstants.ERROR_LOAD_PATCH_VERSION_DIRECTORY_NOT_EXIST:
                ShareTinkerLog.e(TAG, "patch version directory not found, current version:%s", currentVersion);
                tinker.getLoadReporter().onLoadFileNotFound(patchVersionDirectory,
                    ShareConstants.TYPE_PATCH_FILE, true);
                break;

            case ShareConstants.ERROR_LOAD_PATCH_VERSION_FILE_NOT_EXIST:
                ShareTinkerLog.e(TAG, "patch version file not found, current version:%s", currentVersion);
                if (patchVersionFile == null) {
                    throw new TinkerRuntimeException("error load patch version file not exist, but file is null");
                }
                tinker.getLoadReporter().onLoadFileNotFound(patchVersionFile,
                    ShareConstants.TYPE_PATCH_FILE, false);
                break;
            case ShareConstants.ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL:
                ShareTinkerLog.i(TAG, "patch package check fail");
                if (patchVersionFile == null) {
                    throw new TinkerRuntimeException("error patch package check fail , but file is null");
                }
                int errorCode = intentResult.getIntExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_PATCH_CHECK, ShareConstants.ERROR_LOAD_GET_INTENT_FAIL);
                tinker.getLoadReporter().onLoadPackageCheckFail(patchVersionFile, errorCode);
                break;
            case ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_DIRECTORY_NOT_EXIST:
                if (dexDirectory != null) {
                    ShareTinkerLog.e(TAG, "patch dex file directory not found:%s", dexDirectory.getAbsolutePath());
                    tinker.getLoadReporter().onLoadFileNotFound(dexDirectory,
                        ShareConstants.TYPE_DEX, true);
                } else {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Call Tinker.with(app).cleanPatch() to wipe inconsistent patch state, then relaunch without the patch.
  2. Verify the patch directory is only manipulated by tinker (no external 'clear cache' jobs touching /tinker).
  3. Keep tinker gradle plugin and tinker-android-lib versions in lockstep so Intent extras always match.

Example fix

// guard in a startup try/catch
try {
    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
} catch (TinkerRuntimeException e) {
    Tinker.with(context).cleanPatch(); // reset inconsistent patch state
}
Defensive patterns

Strategy: fallback

Validate before calling

// verify patch state consistency before/around applying
File patchDir = Tinker.with(app).getPatchDirectory();
if (patchDir != null && patchDir.exists()) {
    File info = new File(patchDir, "patch.info"); // state must not be half-deleted
    if (!info.exists()) {
        Tinker.with(app).cleanPatch();
    }
}

Try / catch

try {
    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
} catch (TinkerRuntimeException e) {
    Tinker.with(context).cleanPatch(); // reset inconsistent patch state, app runs unpatched
}

Prevention

When it happens

Trigger: The load-result Intent carries ERROR_LOAD_PATCH_VERSION_FILE_NOT_EXIST but no patch-version-file path, so patchVersionFile was never populated when TinkerLoadResult parsed the Intent — e.g. after the patch directory was partially deleted externally while the load result Intent from a previous run persisted.

Common situations: Users or cleanup tools deleting /data/data/<pkg>/tinker contents between the load attempt and result reporting; multi-process races where :patch process rewrites state while the main process reads stale Intent extras; version mismatch between gradle plugin and library producing Intents without the expected extras.

Related errors


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