Tencent/tinker · error · TinkerRuntimeException

Tinker instance is already set.

Error message

Tinker instance is already set.

What it means

Tinker.create(Tinker) is the low-level way to set the singleton (used internally by install/build flows) and throws TinkerRuntimeException("Tinker instance is already set.") if sInstance is already non-null. It exists so the instance can only be bound once per process; a second create indicates duplicated initialization, which Tinker rejects rather than silently replacing configuration.

Source

Thrown at tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/tinker/Tinker.java:119

            throw new TinkerRuntimeException("you must install tinker before get tinker sInstance");
        }
        synchronized (Tinker.class) {
            if (sInstance == null) {
                sInstance = new Builder(context).build();
            }
        }
        return sInstance;
    }

    /**
     * create custom tinker by {@link Tinker.Builder}
     * please do it when very first your app start.
     *
     * @param tinker
     */
    public static void create(Tinker tinker) {
        if (sInstance != null) {
            throw new TinkerRuntimeException("Tinker instance is already set.");
        }
        sInstance = tinker;
    }

    public static boolean isTinkerInstalled() {
        return sInstalled;
    }

    /**
     * you must install tinker first!!
     *
     * @param intentResult
     * @param serviceClass
     * @param upgradePatch
     */
    public void install(Intent intentResult, Class<? extends AbstractResultService> serviceClass,
                        AbstractPatch upgradePatch) {
        sInstalled = true;

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Pick exactly one initialization path per process: prefer TinkerInstaller.install(...) and delete direct create() calls.
  2. If you must use create(), guard it: only call when the instance is not already set (track with your own static flag or wrap in try-catch during migration).
  3. Move initialization to the earliest single point (Application.attachBaseContext/onCreate) so later re-entry cannot occur.

Example fix

// before
TinkerInstaller.install(appLike, ...);   // sets sInstance
...
Tinker.create(new Tinker.Builder(app).build()); // throws: already set

// after
TinkerInstaller.install(appLike, ...);   // single init path
// remove the manual Tinker.create(...) call entirely
Defensive patterns

Strategy: validation

Validate before calling

// use the official installer only, exactly once per process
if (!Tinker.isTinkerInstalled()) {
    TinkerInstaller.install(appLike, loadReporter, patchReporter, listener,
            null, loadVerifyFlag, null);
}
// do not call Tinker.create(...) directly

Prevention

When it happens

Trigger: Calling Tinker.create(...) twice in the same process — e.g. both TinkerInstaller.install() and a manual Builder(...).build()/create() path running, or install invoked from two lifecycle callbacks, or a retained client library re-initializing.

Common situations: Migrating from manual Builder+create setup to TinkerInstaller.install and leaving the old path in place; initializing in both a base Application class and a subclass's onCreate; SDKs that embed Tinker initializing on top of the app's own initialization.

Related errors


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