Tencent/tinker · error · TinkerRuntimeException

tinkerLoadVerifyFlag is already set.

Error message

tinkerLoadVerifyFlag is already set.

What it means

Tinker.Builder.tinkerLoadVerifyFlag(Boolean) is a set-once fluent setter: after the null check it throws TinkerRuntimeException("tinkerLoadVerifyFlag is already set.") when the field has already been assigned. Like the other builder guards, this prevents two different code paths from each configuring the verify flag, which would silently change whether patches are md5-checked at load time.

Source

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

            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) {
                throw new TinkerRuntimeException("loadReporter must not be null.");
            }
            if (this.loadReporter != null) {
                throw new TinkerRuntimeException("loadReporter is already set.");
            }
            this.loadReporter = loadReporter;
            return this;
        }

        public Builder patchReporter(PatchReporter patchReporter) {
            if (patchReporter == null) {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Resolve the final flag once (prefer the stricter true when in doubt) and call the setter a single time.
  2. Compute the decision before building: boolean verify = isQa || cfgFlag; builder.tinkerLoadVerifyFlag(verify).
  3. Create a fresh Builder per initialization and never share/reuse it across code paths.

Example fix

// before
builder.tinkerLoadVerifyFlag(true);          // QA default
if (!isQa) builder.tinkerLoadVerifyFlag(false); // throws: already set

// after
builder.tinkerLoadVerifyFlag(isQa); // single decision, one call
Defensive patterns

Strategy: validation

Validate before calling

boolean verify = isQaBuild || Boolean.TRUE.equals(cfgFlag);
builder.tinkerLoadVerifyFlag(verify); // single call

Prevention

When it happens

Trigger: Calling tinkerLoadVerifyFlag(...) twice on the same Builder — e.g. a shared init helper sets true for QA builds and the app's own chain then sets false, or a duplicated chain line after merge/refactor.

Common situations: Debug/QA overlays that 'adjust' a base configuration; builder kept as a long-lived field receiving repeated configuration; two SDKs both embedding Tinker init logic.

Related errors


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