didi/DoKit · error · IOException

Failed to decode stream.

Error message

Failed to decode stream.

What it means

BitmapHunter.decodeStream wraps BitmapFactory.decodeStream: when the factory returns null (it reports decode failures as null rather than throwing), the hunter converts it into IOException('Failed to decode stream.') so the request is treated as a retrievable/retryable IO failure. Null comes from data that is not a decodable image, a truncated stream, or an in-sample/decode-options mismatch. Because it is an IOException, Picasso routes it to the error path (error drawable / listener) rather than crashing the app.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java:145

      byte[] bytes = Utils.toByteArray(stream);
      if (calculateSize) {
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
            request);
      }
      return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    } else {
      if (calculateSize) {
        BitmapFactory.decodeStream(stream, null, options);
        RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
            request);

        markStream.reset(mark);
      }
      Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
      if (bitmap == null) {
        // Treat null as an IO exception, we will eventually retry.
        throw new IOException("Failed to decode stream.");
      }
      return bitmap;
    }
  }

  @Override public void run() {
    try {
      updateThreadName(data);

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
      }

      result = hunt();

      if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {

View on GitHub (pinned to 626827cddb)

Solutions

  1. Pull the URL separately (OkHttp/curl) and confirm the bytes are actually a decodable image (file type, not an HTML error page)
  2. Attach an error fallback so the UI degrades gracefully: .error(R.drawable.broken).into(view)
  3. For custom sources, return a fresh, mark-supported stream from your RequestHandler (use Utils.readFileToBuffer-style buffering or ByteArrayInputStream)
  4. Re-encode problematic assets (CMYK JPEG -> RGB PNG) or downscale server-side
  5. If intermittent, rely on Picasso's retry semantics or add .memoryPolicy(MemoryPolicy.NO_CACHE) once to force a clean fetch

Example fix

// before
DokitPicasso.with(context).load(url).into avatarView;
// IOException: Failed to decode stream. -> blank view

// after
DokitPicasso.with(context)
    .load(url)
    .error(R.drawable.avatar_placeholder)
    .into(avatarView, new com.squareup.picasso.Callback.EmptyCallback() {});
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-flight check when the bytes come from your own source (optional):
static boolean isDecodable(byte[] bytes) {
  BitmapFactory.Options o = new BitmapFactory.Options();
  o.inJustDecodeBounds = true;
  BitmapFactory.decodeByteArray(bytes, 0, bytes.length, o);
  return o.outWidth > 0 && o.outHeight > 0;
}

Try / catch

// It is an IOException routed to the error path — handle via callback + error drawable:
picasso.load(url)
    .error(R.drawable.broken)
    .into(view, new Callback() {
      @Override public void onSuccess() {}
      @Override public void onError(Exception e) {
        if (e instanceof IOException && "Failed to decode stream.".equals(e.getMessage())) {
          reportBadImage(url); // e.g. fetch and inspect bytes server-side
        }
      }
    });

Prevention

When it happens

Trigger: Server returns 200 with HTML/error page bytes for an image URL; progressive JPEG or WebP variant unsupported by BitmapFactory on the API level; stream consumed twice without the mark/reset path (custom RequestHandler returning an already-read stream); resized decode where calculateInSampleSize produced invalid options.

Common situations: CDN serving an error page or truncated image on flaky networks; CMYK JPEGs; images with unusual EXIF; OOM-adjacent very large images where decode silently returns null.

Understand the failure class

Related errors


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