Tencent/tinker · error · TinkerRuntimeException
error patch package check fail , but file is null
Error message
error patch package check fail , but file is null
What it means
Tinker throws this TinkerRuntimeException while dispatching the patch-load result code ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL in TinkerLoadResult. That result code means the patch package failed its security/integrity check during loading, and the normal path is to report onLoadPackageCheckFail(patchVersionFile, errorCode) to the app's LoadReporter. The exception fires only when the patchVersionFile is null at that point, which is an internal invariant violation: the load pipeline produced a 'package check failed' result but no longer knows which versioned patch file to blame.
Source
Thrown at tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/tinker/TinkerLoadResult.java:188
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 {
//should be not here
ShareTinkerLog.e(TAG, "patch dex file directory not found, warning why the path is null!!!!");
throw new TinkerRuntimeException("patch dex file directory not found, warning why the path is null!!!!");
}
break;
case ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_FILE_NOT_EXIST:
String dexPath = ShareIntentUtil.getStringExtra(intentResult,
ShareIntentUtil.INTENT_PATCH_MISSING_DEX_PATH);View on GitHub (pinned to 1b7ea02c23)
Solutions
- Ensure cleanPatch()/onPatchRollback() is never invoked while a load is in flight; serialize patch lifecycle operations through one process (usually the :patch process or main process only).
- Verify the patch info file and /tinker/<version>/ directory still exist before triggering a load attempt; re-install or clean the patch from a LoadReporter callback such as onLoadFileNotFound.
- Upgrade to the latest Tinker version, where several null-invariant races in TinkerLoadResult were hardened.
- If reproducible, capture the tinker log (ShareTinkerLog) around the crash and check whether the version directory was removed mid-load.
Defensive patterns
Strategy: try-catch
Validate before calling
File infoFile = new File(context.getFilesDir().getParent() + "/tinker/patch_info");
File versionDir = new File(new File(infoFile.getParent(), "patch-" + currentVersion), "");
if (!infoFile.exists() || !versionDir.isDirectory()) {
Tinker.with(context).cleanPatch(); // do not attempt load on inconsistent state
} Try / catch
try {
Tinker.with(context).loadPatch();
} catch (TinkerRuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("file is null")) {
Tinker.with(context).cleanPatch(); // state inconsistent, reset
} else { throw e; }
} Prevention
- Never call cleanPatch() while a patch load or apply is in flight
- Drive all patch lifecycle calls from a single process
- Keep tinker lib, loader and plugin versions aligned
When it happens
Trigger: A patch install attempt returns result code ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL from the loader intent, and simultaneously patchVersionFile (looked up from patch info / version directory) resolves to null. Typically caused by the patch version directory or info file being deleted (e.g. by cleanPatch, another process, or a cleanup service) between the load attempt and result handling.
Common situations: Calling TinkerInstaller.cleanPatch() or Tinker.with(ctx).cleanPatch() concurrently with a patch load; two processes racing to load/clean the same patch; a corrupted or half-written /tinker/patch info state after a killed process; disk-cleanup tools removing tinker directories.
Related errors
- patch ${type} extract failed (${message}).
- patch %s extract failed (%s).
- patch dex file directory not found, warning why the path is
- patch dex file not found, but path is null!!!!
- patch dex opt file not found, but path is null!!!!
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/d0d78344ca92053e.
Report an issue: GitHub.