didi/DoKit · error · IllegalStateException

Cannot set the LeakDirectoryProvider after it has already be

Error message

Cannot set the LeakDirectoryProvider after it has already been set. Try setting it before installing the RefWatcher.

What it means

LeakCanaryInternals.setLeakDirectoryProvider() sets the process-wide directory provider used to store heap dumps. Because the analyzer, activities, and services all read this static field, it can only be set once; setting it after the RefWatcher is installed risks half the components using the old location, so a second set throws IllegalStateException.

Source

Thrown at Android/dokit-leakcanary/src/main/java/com/squareup/leakcanary/internal/LeakCanaryInternals.java:198

        notificationManager.createNotificationChannel(notificationChannel);
      }
      builder.setChannelId(NOTIFICATION_CHANNEL_ID);
    }

    if (SDK_INT < JELLY_BEAN) {
      return builder.getNotification();
    } else {
      return builder.build();
    }
  }

  public static Executor newSingleThreadExecutor(String threadName) {
    return Executors.newSingleThreadExecutor(new LeakCanarySingleThreadFactory(threadName));
  }

  public static void setLeakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider) {
    if (LeakCanaryInternals.leakDirectoryProvider != null) {
      throw new IllegalStateException("Cannot set the LeakDirectoryProvider after it has already "
          + "been set. Try setting it before installing the RefWatcher.");
    }
    LeakCanaryInternals.leakDirectoryProvider = leakDirectoryProvider;
  }

  public static LeakDirectoryProvider getLeakDirectoryProvider(Context context) {
    LeakDirectoryProvider leakDirectoryProvider = LeakCanaryInternals.leakDirectoryProvider;
    if (leakDirectoryProvider == null) {
      leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
    }
    return leakDirectoryProvider;
  }

  private LeakCanaryInternals() {
    throw new AssertionError();
  }
}

View on GitHub (pinned to 626827cddb)

Solutions

  1. Call setLeakDirectoryProvider before buildAndInstall(), in the same single init block
  2. Check the current state first: only set when LeakCanaryInternals-installed watcher is null / provider not yet set
  3. If reconfiguration is genuinely needed, do it in a fresh process rather than at runtime

Example fix

// before
LeakCanary.refWatcher(this).buildAndInstall();
LeakCanaryInternals.setLeakDirectoryProvider(customProvider); // throws

// after
LeakCanaryInternals.setLeakDirectoryProvider(customProvider);
LeakCanary.refWatcher(this).buildAndInstall();
Defensive patterns

Strategy: validation

Validate before calling

// in the single init block, before install:
LeakCanaryInternals.setLeakDirectoryProvider(customProvider);
LeakCanary.refWatcher(context).buildAndInstall();

Prevention

When it happens

Trigger: Calling LeakCanaryInternals.setLeakDirectoryProvider(provider) after a previous call, in particular after LeakCanary's buildAndInstall() already ran (installation itself configures the provider). Typical in debug tool setups (e.g. DoraemonKit panel) that let the user change the heap dump location at runtime.

Common situations: App initializes LeakCanary in Application.onCreate, then a debug menu tries to redirect heap dumps to a custom folder. Two libraries both attempting to configure the provider. Tests that reconfigure per test method without process reset.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/49b46c9c0513bfff. Report an issue: GitHub.