Tencent/tinker · error · TinkerRuntimeException

tinkerFlag is already set.

Error message

tinkerFlag is already set.

What it means

Builder.tinkerFlags(int) may only be called once. The builder tracks whether status was already assigned (status != -1) and throws on a second call to prevent ambiguous flag configuration (e.g., one call enabling only dex, another enabling only so).

Solutions

  1. Set tinkerFlags exactly once, computing the final mask before calling the builder.
  2. Compose flags in a variable (dex | native | res as needed) rather than calling the setter repeatedly.
  3. Create a fresh Builder per initialization instead of reusing a stored one.

Example fix

// before
builder.tinkerFlags(TINKER_DEX);
builder.tinkerFlags(TINKER_NATIVE); // throws

// after
int flags = ShareConstants.TINKER_DEX | ShareConstants.TINKER_NATIVE;
builder.tinkerFlags(flags);
Defensive patterns

Strategy: validation

Validate before calling

int flags = 0;
if (dexEnabled) flags |= ShareConstants.TINKER_DEX;
if (nativeEnabled) flags |= ShareConstants.TINKER_NATIVE;
if (resEnabled) flags |= ShareConstants.TINKER_RESOURCES;
builder.tinkerFlags(flags); // single call with the composed mask

Prevention

When it happens

Trigger: Chaining or repeating tinkerFlags(...) twice on the same Builder instance — commonly when one shared builder is reused across init paths, or a helper method adds flags in addition to explicit call-site configuration.

Common situations: A base init method sets tinkerFlags(TINKER_DEX) and a feature module calls tinkerFlags(TINKER_DEX | TINKER_NATIVE) on the same builder; copy-pasted config blocks; builders stored as fields and reused after orientation/process re-entry.

Related errors


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

Appendix: source

Thrown at tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/tinker/Tinker.java:367

            if (context == null) {
                throw new TinkerRuntimeException("Context must not be null.");
            }
            this.context = context;
            this.mainProcess = TinkerServiceInternals.isInMainProcess(context);
            this.patchProcess = TinkerServiceInternals.isInTinkerPatchServiceProcess(context);
            this.patchDirectory = SharePatchFileUtil.getPatchDirectory(context);
            if (this.patchDirectory == null) {
                ShareTinkerLog.e(TAG, "patchDirectory is null!");
                return;
            }
            this.patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory.getAbsolutePath());
            this.patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory.getAbsolutePath());
            ShareTinkerLog.w(TAG, "tinker patch directory: %s", patchDirectory);
        }

        public Builder tinkerFlags(int tinkerFlags) {
            if (this.status != -1) {
                throw new TinkerRuntimeException("tinkerFlag is already set.");
            }
            this.status = tinkerFlags;
            return this;
        }

        public Builder tinkerLoadVerifyFlag(Boolean verifyMd5WhenLoad) {
            if (verifyMd5WhenLoad == null) {
                throw new TinkerRuntimeException("tinkerLoadVerifyFlag must not be null.");
            }
            if (this.tinkerLoadVerifyFlag != null) {
                throw new TinkerRuntimeException("tinkerLoadVerifyFlag is already set.");
            }
            this.tinkerLoadVerifyFlag = verifyMd5WhenLoad;
            return this;
        }

        public Builder loadReport(LoadReporter loadReporter) {
            if (loadReporter == null) {

View on GitHub (pinned to 1b7ea02c23)