didi/DoKit · error · IllegalArgumentException

Downloader must not be null.

Error message

Downloader must not be null.

What it means

Builder.downloader(Downloader) sets the network layer; null is rejected with IllegalArgumentException('Downloader must not be null.') (and a second call with IllegalStateException 'Downloader already set.'). Picasso needs a concrete downloader to fetch remote URLs — there is no default-null mode — so the guard fires at build configuration time rather than at first network request.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/DokitPicasso.java:722

      this.context = context.getApplicationContext();
    }

    /**
     * Specify the default {@link Bitmap.Config} used when decoding images. This can be overridden
     * on a per-request basis using {@link RequestCreator#config(Bitmap.Config) config(..)}.
     */
    public Builder defaultBitmapConfig(Bitmap.Config bitmapConfig) {
      if (bitmapConfig == null) {
        throw new IllegalArgumentException("Bitmap config must not be null.");
      }
      this.defaultBitmapConfig = bitmapConfig;
      return this;
    }

    /** Specify the {@link Downloader} that will be used for downloading images. */
    public Builder downloader(Downloader downloader) {
      if (downloader == null) {
        throw new IllegalArgumentException("Downloader must not be null.");
      }
      if (this.downloader != null) {
        throw new IllegalStateException("Downloader already set.");
      }
      this.downloader = downloader;
      return this;
    }

    /**
     * Specify the executor service for loading images in the background.
     * <p>
     * Note: Calling {@link DokitPicasso#shutdown() shutdown()} will not shutdown supplied executors.
     */
    public Builder executor(ExecutorService executorService) {
      if (executorService == null) {
        throw new IllegalArgumentException("Executor service must not be null.");
      }
      if (this.service != null) {

View on GitHub (pinned to 626827cddb)

Solutions

  1. Always pass a real Downloader: new OkHttp3Downloader(client) or new UrlConnectionDownloader()
  2. If DI provides it, make the provider non-nullable and fail at graph construction
  3. To block network in tests, supply a Downloader whose load() throws IOException — not null

Example fix

// before
new DokitPicasso.Builder(context)
    .downloader(injectedDownloader) // null when DI module missed
    .build();

// after
new DokitPicasso.Builder(context)
    .downloader(injectedDownloader != null
        ? injectedDownloader
        : new okhttp3.OkHttp3Downloader(new OkHttpClient()))
    .build();
Defensive patterns

Strategy: validation

Validate before calling

Downloader dl = (injectedDownloader != null)
    ? injectedDownloader
    : new okhttp3.OkHttp3Downloader(new OkHttpClient());
DokitPicasso picasso = new DokitPicasso.Builder(context).downloader(dl).build();

Try / catch

try {
  builder.downloader(downloader);
} catch (IllegalArgumentException e) {
  if ("Downloader must not be null.".equals(e.getMessage())) {
    builder.downloader(new okhttp3.OkHttp3Downloader(new OkHttpClient()));
  } else throw e;
}

Prevention

When it happens

Trigger: builder.downloader(null) from an injection field that is not yet populated; passing a Downloader obtained from a lazy provider that returned null; test code stubbing the downloader as null to 'disable' networking (use a mock that throws instead).

Common situations: DI module supplying null when a flavor lacks the OkHttp dependency; replacing UrlConnectionDownloader with a custom one whose factory fails silently.

Related errors


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