didi/DoKit · error · IllegalStateException
Transformation " + transformation.key() + " returned input B
Error message
Transformation " + transformation.key() + " returned input Bitmap but recycled it.
What it means
A Transformation returned the same Bitmap instance it was given (newResult == result) but that bitmap is now recycled. Picasso's contract lets a transformation return the input bitmap only if it is still usable; returning a recycled input is a memory-corruption hazard, so Picasso posts IllegalStateException('... returned input Bitmap but recycled it.') on the main thread and fails the request.
Source
Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java:461
.append(transformation.key())
.append(" returned null after ")
.append(i)
.append(" previous transformation(s).\n\nTransformation list:\n");
for (Transformation t : transformations) {
builder.append(t.key()).append('\n');
}
DokitPicasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new NullPointerException(builder.toString());
}
});
return null;
}
if (newResult == result && result.isRecycled()) {
DokitPicasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " returned input Bitmap but recycled it.");
}
});
return null;
}
// If the transformation returned a new bitmap ensure they recycled the original.
if (newResult != result && !result.isRecycled()) {
DokitPicasso.HANDLER.post(new Runnable() {
@Override public void run() {
throw new IllegalStateException("Transformation "
+ transformation.key()
+ " mutated input Bitmap but failed to recycle the original.");
}
});
return null;
}View on GitHub (pinned to 626827cddb)
Solutions
- Never recycle the input bitmap in transform(); Picasso owns it
- If you create a new bitmap, recycle the input and return the NEW bitmap (that is the legal pattern — see error 87)
- Audit any src.recycle() call inside Transformation implementations and delete it when src is returned
Example fix
// before
@Override public Bitmap transform(Bitmap src) {
Bitmap out = Bitmap.createScaledBitmap(src, 100, 100, true);
src.recycle();
return src; // recycled input returned -> IllegalStateException
}
// after
@Override public Bitmap transform(Bitmap src) {
Bitmap out = Bitmap.createScaledBitmap(src, 100, 100, true);
if (out != src) src.recycle();
return out; // return the new bitmap
} Defensive patterns
Strategy: validation
Validate before calling
// Canonical safe pattern: return the input un-recycled, or a new bitmap with input recycled.
static Bitmap finishTransform(Bitmap src, Bitmap out) {
if (out == src && src.isRecycled()) throw new AssertionError("input recycled"); // catch in dev
return out;
} Try / catch
// Posted to the main thread by Picasso; cannot be caught around .into(). // Fix the transformation source: // WRONG: src.recycle(); return src; // RIGHT: Bitmap out = ...; if (out != src) src.recycle(); return out;
Prevention
- Only two legal endings: return src (unrecycled) or return new bitmap with src recycled
- Code-review every recycle() call inside transformations
- Write a unit test asserting the returned bitmap's isRecycled() == false
When it happens
Trigger: Transform code that calls src.recycle() and then returns src; a helper that recycles 'temporaries' but includes the input; double transformations where the first recycles and the second returns it.
Common situations: Over-aggressive memory optimization copying the 'recycle what you allocate' pattern onto the input bitmap; merging code from two transformations with different recycle conventions.
Related errors
- Transformation <transformation.key()> mutated input Bitmap b
- Transformation " + transformation.key() + " crashed with exc
- Transformation " + transformation.key() + " returned null af
- Bitmap config must not be null.
- Target callback must not recycle bitmap!
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/d4962f6965be77c2.
Report an issue: GitHub.