Tencent/tinker · critical · TinkerRuntimeException

upgradePatchProcessor is null.

Error message

upgradePatchProcessor is null.

What it means

Inside TinkerPatchService.doApplyPatch: the service is running, but the static upgradePatchProcessor was never set. That field is assigned by Tinker.install(...) -> TinkerPatchService.setPatchProcessor(...). So this throw means the :patch process is alive and handling an apply intent while Tinker was never installed in that process — an initialization-order/integration bug.

Source

Thrown at tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/TinkerPatchService.java:236

            }
            String path = getPatchPathExtra(intent);
            if (path == null) {
                ShareTinkerLog.e(TAG, "TinkerPatchService can't get the path extra, ignoring.");
                return;
            }
            File patchFile = new File(path);

            final boolean useEmergencyMode = getPatchUseEmergencyMode(intent);

            long begin = SystemClock.elapsedRealtime();
            boolean result;
            long cost;
            Throwable e = null;

            PatchResult patchResult = new PatchResult();
            try {
                if (upgradePatchProcessor == null) {
                    throw new TinkerRuntimeException("upgradePatchProcessor is null.");
                }
                result = upgradePatchProcessor.tryPatch(context, path, useEmergencyMode, patchResult);
            } catch (Throwable throwable) {
                e = throwable;
                result = false;
                tinker.getPatchReporter().onPatchException(patchFile, e);
            }

            cost = SystemClock.elapsedRealtime() - begin;
            tinker.getPatchReporter()
                    .onPatchResult(patchFile, result, cost);

            patchResult.isSuccess = result;
            patchResult.rawPatchFilePath = path;
            patchResult.useEmergencyMode = useEmergencyMode;
            patchResult.totalCostTime = cost;
            patchResult.type = tinker.getCustomPatcher() == null ? PatchResult.PATCH_TYPE_BSDIFF : PatchResult.PATCH_TYPE_CUSTOM;
            patchResult.e = e;

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Call TinkerInstaller.install unconditionally in your Application/DefaultApplicationLike.onCreate for every process, including the patch process.
  2. Do not gate install on runtime flags; install a no-op configuration instead of skipping install.
  3. If you catch exceptions around install, rethrow or disable the patch service so it never runs half-initialized.
  4. Ensure the tinker patch process is declared and named exactly as configured so the Application executes there too.

Example fix

// before
if (remoteConfig.tinkerEnabled) {
    TinkerInstaller.install(appLike);
}

// after
TinkerInstaller.install(appLike); // always install; control enablement via tinkerFlags/isTinkerEnabled instead
Defensive patterns

Strategy: validation

Validate before calling

if (!Tinker.isTinkerInstalled()) {
    // cannot apply patches yet: defer instead of starting the service
    deferPatchWorkUntilInstall();
    return;
}
TinkerInstaller.onReceiveUpgradePatch(context, patchPath);

Type guard

public static boolean canApplyPatches() {
    return Tinker.isTinkerInstalled()
        && Tinker.isTinkerEnabled()
        && TinkerServiceInternals.isInMainProcess(appContext);
}

Try / catch

try {
    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
} catch (Throwable t) {
    // upgradePatchProcessor null surfaces here as TinkerRuntimeException
    reportPatchApplyFailure(t);
}

Prevention

When it happens

Trigger: TinkerPatchService.onHandleIntent receives a valid apply intent, but Tinker.install(context, ...) (or TinkerInstaller.install) has not executed in this process — because install was skipped, thrown earlier, or the service runs in a process whose Application path never installs tinker.

Common situations: The :patch process was started (e.g., by a sticky redelivery or an app-external component) before Application.onCreate ran install; a conditional install (only when a server flag is set) leaves tinker uninstalled while a patch intent arrives; install threw earlier and the app caught it, leaving the service unconfigured.

Related errors


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