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).
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)
Solutions
- Set tinkerFlags exactly once, computing the final mask before calling the builder.
- Compose flags in a variable (dex | native | res as needed) rather than calling the setter repeatedly.
- 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
- Compose the flag mask in a local variable and call tinkerFlags once.
- Keep builder configuration in one method owned by the app module.
- Create a new Builder instance per initialization instead of reusing stored ones.
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
- tinkerLoadVerifyFlag is already set.
- tinkerFlag is already set.
- tinkerLoadVerifyFlag must not be null.
- tinkerLoadVerifyFlag is already set.
- loadReporter is already set.
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/b6ef6fceb7790f21.
Report an issue: GitHub.