didi/DoKit · error · UnsupportedOperationException
leakTraceAsFakeException() can only be called when leakFound
Error message
leakTraceAsFakeException() can only be called when leakFound is true
What it means
Thrown by AnalysisResult.leakTraceAsFakeException() when it is invoked on an AnalysisResult whose leakFound field is false. The method fabricates a RuntimeException whose stack trace mirrors the leak trace (leaking class <- holder chain); that only makes sense when analysis actually found a leak. It is a guard against calling a result accessor on a non-leak result.
Source
Thrown at Android/dokit-leakcanary/src/main/java/com/squareup/leakcanary/AnalysisResult.java:110
* <pre>
* * com.foo.WibbleActivity has leaked:
* * GC ROOT static com.foo.Bar.qux
* * references com.foo.Quz.context
* * leaks com.foo.WibbleActivity instance
* </pre>
*
* <p>Will turn into an exception with the following stacktrace:
* <pre>
* java.lang.RuntimeException: com.foo.WibbleActivity leak from com.foo.Bar (holder=CLASS,
* type=STATIC_FIELD)
* at com.foo.Bar.qux(Bar.java:42)
* at com.foo.Quz.context(Quz.java:42)
* at com.foo.WibbleActivity.leaking(WibbleActivity.java:42)
* </pre>
*/
public @NonNull RuntimeException leakTraceAsFakeException() {
if (!leakFound) {
throw new UnsupportedOperationException(
"leakTraceAsFakeException() can only be called when leakFound is true");
}
LeakTraceElement firstElement = leakTrace.elements.get(0);
String rootSimpleName = classSimpleName(firstElement.className);
String leakSimpleName = classSimpleName(className);
String exceptionMessage = leakSimpleName
+ " leak from "
+ rootSimpleName
+ " (holder="
+ firstElement.holder
+ ", type="
+ firstElement.type
+ ")";
RuntimeException exception = new RuntimeException(exceptionMessage);
StackTraceElement[] stackTrace = new StackTraceElement[leakTrace.elements.size()];
int i = 0;View on GitHub (pinned to 626827cddb)
Solutions
- Check result.leakFound before calling leakTraceAsFakeException()
- Branch on the full result state: leakFound, failureText (analysis failed) before touching leakTrace
- If you need the leak trace outside the exception, access result.leakTrace directly (also only when leakFound is true)
Example fix
// before
RuntimeException fake = result.leakTraceAsFakeException();
// after
if (result.leakFound) {
RuntimeException fake = result.leakTraceAsFakeException();
crashReporter.report(fake);
} else if (result.failureText != null) {
Log.e("LeakCanary", result.failureText);
} Defensive patterns
Strategy: validation
Validate before calling
if (result.leakFound) { RuntimeException fake = result.leakTraceAsFakeException(); } Prevention
- Always branch on result.leakFound and result.failureText before accessing leak-specific accessors
- Treat leakTraceAsFakeException as available only on positive results
When it happens
Trigger: Calling analysisResult.leakTraceAsFakeException() without first checking analysisResult.leakFound. Typical in code that unconditionally converts every analysis result into an exception for crash reporting or test assertions, including results that reported NO_LEAK or failed analysis.
Common situations: Integrating LeakCanary results into a crash reporter and forgetting the leakFound branch. Writing unit tests over AnalysisResult that exercise the fake-exception path for all results. Handling AnalysisResult.failureText / retention-visibility results where leakFound is false.
Related errors
- buildAndInstall() should only be called once.
- maxStoredHeapDumps must be at least 1
- Could not find char array in {}
- File does not exist: {}
- Could not find the {} class in the heap dump.
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/8310e89c76511181.
Report an issue: GitHub.