Tencent/tinker · error · TinkerRuntimeException
tinkerFlag is already set.
Error message
tinkerFlag is already set.
What it means
Tinker.Builder.tinkerFlags(int) is a set-once fluent setter: it throws TinkerRuntimeException("tinkerFlag is already set.") when this.status != -1, i.e. when the same builder has already had its flags configured. The guard catches accidental double-configuration from chaining or repeated calls, which would otherwise silently override the enabled-patch-type bitmask (dex, native lib, resources).
Source
Thrown at tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/tinker/Tinker.java:316
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)
Solutions
- Decide the bitmask once and pass it as a parameter to a single tinkerFlags() call.
- If defaults plus overrides are needed, compute the final int (e.g. flags &= ~Tinker_ENABLE_NATIVE_LIBRARY) before calling the setter once.
- Never reuse a Builder instance; construct a fresh one per initialization.
Example fix
// before builder.tinkerFlags(TinkerENABLE.ALL); if (debugBuild) builder.tinkerFlags(TinkerENABLE.DEX_ONLY); // throws // after int flags = debugBuild ? TinkerENABLE.DEX_ONLY : TinkerENABLE.ALL; builder.tinkerFlags(flags);
Defensive patterns
Strategy: validation
Validate before calling
// compute final flags once, then a single setter call int flags = TinkerENABLE.ALL; if (!nativePatchingEnabled) flags &= ~TinkerENABLE.NATIVE_LIBRARY; builder.tinkerFlags(flags);
Prevention
- Keep the whole builder chain in one method
- Resolve configuration before touching the builder, not on it
When it happens
Trigger: Calling tinkerFlags(...) twice on the same Builder instance — e.g. shared configuration code applying a default bitmask and then caller-specific code applying another, or a copy-pasted chain segment duplicated during refactor.
Common situations: A common base method configures TinkerEnabled.ALL and a flavor-specific path then narrows it to TinkerDISABLE or DEX_ONLY; builder instances cached in a field and reused; merging two init code paths during cleanup.
Related errors
- tinkerLoadVerifyFlag must not be null.
- tinkerLoadVerifyFlag is already set.
- loadReporter is already set.
- tinkerFlag is already set.
- tinkerLoadVerifyFlag must not be null.
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/7d803b91bad4104e.
Report an issue: GitHub.