Tencent/tinker · error · TinkerRuntimeException

loadReporter is already set.

Error message

loadReporter is already set.

What it means

Tinker.Builder.loadReport(LoadReporter) throws TinkerRuntimeException("loadReporter is already set.") on the second invocation — the reporter is a set-once builder property. This mirrors the other guards: load reporting must have exactly one owner, and re-assigning it (e.g. base config plus app config) would silently drop one set of callbacks.

Source

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

        }

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

        public Builder listener(PatchListener listener) {
            if (listener == null) {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Register exactly one LoadReporter — the app's custom one if it exists, otherwise DefaultLoadReporter — in a single loadReport() call.
  2. If a base implementation plus extra behavior is needed, wrap both in one composite LoadReporter and register the composite.
  3. Keep the entire builder chain in one method to make duplicate calls visually obvious.

Example fix

// before
builder.loadReport(new DefaultLoadReporter(app)); // base init
builder.loadReport(new MyAppLoadReporter(app));   // throws: already set

// after
LoadReporter reporter = useCustom ? new MyAppLoadReporter(app) : new DefaultLoadReporter(app);
builder.loadReport(reporter);
Defensive patterns

Strategy: validation

Validate before calling

LoadReporter reporter = useCustom ? new MyAppLoadReporter(app) : new DefaultLoadReporter(app);
builder.loadReport(reporter); // exactly one call

Prevention

When it happens

Trigger: Calling loadReport(...) twice on the same Builder instance — shared init helper applying a default reporter plus the app applying its custom one, or a duplicated chain line left behind after refactoring initialization code.

Common situations: A common module configures DefaultLoadReporter and the host app wants a custom LoadReporter; merging Tinker init from two teams/SDKs; builder stored in a static field and configured in stages.

Related errors


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