{"record":{"id":"504a817b8c06f6f4","repo":"didi/DoKit","slug":"transformation-transformation-key-mutated-inpu","errorCode":null,"errorMessage":"Transformation <transformation.key()> mutated input Bitmap but failed to recycle the original.","messagePattern":"Transformation <transformation\\.key\\(\\)> mutated input Bitmap but failed to recycle the original\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java","lineNumber":473,"sourceCode":"        return null;\n      }\n\n      if (newResult == result && result.isRecycled()) {\n        DokitPicasso.HANDLER.post(new Runnable() {\n          @Override public void run() {\n            throw new IllegalStateException(\"Transformation \"\n                + transformation.key()\n                + \" returned input Bitmap but recycled it.\");\n          }\n        });\n        return null;\n      }\n\n      // If the transformation returned a new bitmap ensure they recycled the original.\n      if (newResult != result && !result.isRecycled()) {\n        DokitPicasso.HANDLER.post(new Runnable() {\n          @Override public void run() {\n            throw new IllegalStateException(\"Transformation \"\n                + transformation.key()\n                + \" mutated input Bitmap but failed to recycle the original.\");\n          }\n        });\n        return null;\n      }\n\n      result = newResult;\n    }\n    return result;\n  }\n\n  static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {\n    int inWidth = result.getWidth();\n    int inHeight = result.getHeight();\n    boolean onlyScaleDown = data.onlyScaleDown;\n\n    int drawX = 0;","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java#L455-L491","documentation":"The complementary guard to error 86: when a Transformation returns a DIFFERENT bitmap (newResult != result), Picasso requires the original input to have been recycled. If result.isRecycled() is still false, it posts IllegalStateException('... mutated input Bitmap but failed to recycle the original.') and fails the request. This leaks otherwise — Picasso's memory model expects transformations to hand ownership of exactly one bitmap back.","triggerScenarios":"A transform that does Bitmap out = Bitmap.createBitmap(...); return out; without calling src.recycle(); returning a cached/derived bitmap while leaving the input alive.","commonSituations":"Transformations written for Glide or plain BitmapFactory code where the caller manages recycling; forgetting the recycle after refactoring a transform to return a new instance.","solutions":["Add src.recycle() whenever you return a bitmap other than src","Use the canonical pattern: Bitmap out = ...; if (out != src) src.recycle(); return out;","If you must keep the input alive for caching, return src (unmodified) instead and copy inside your cache logic"],"exampleFix":"// before\n@Override public Bitmap transform(Bitmap src) {\n  return Bitmap.createScaledBitmap(src, 200, 200, true); // src never recycled\n}\n\n// after\n@Override public Bitmap transform(Bitmap src) {\n  Bitmap out = Bitmap.createScaledBitmap(src, 200, 200, true);\n  if (out != src) src.recycle();\n  return out;\n}","handlingStrategy":"validation","validationCode":"// Use one helper for the ownership handoff so the pattern is never forgotten:\nstatic Bitmap handoff(Bitmap src, Bitmap out) {\n  if (out != src && !src.isRecycled()) src.recycle();\n  return out;\n}\n// in transform(): return handoff(src, Bitmap.createScaledBitmap(src, w, h, true));","typeGuard":null,"tryCatchPattern":"// Main-thread throw from Picasso; prevention lives inside the Transformation:\n@Override public Bitmap transform(Bitmap src) {\n  Bitmap out = Bitmap.createBitmap(src, 0, 0, w, h);\n  if (out != src) src.recycle(); // required\n  return out;\n}","preventionTips":["Whenever transform returns a different instance, recycle the input in the same statement block","Centralize the 'create + recycle + return' pattern in a shared utility","Do not keep references to the input bitmap after returning a new one"],"tags":["android","picasso","bitmap","recycle","transformation"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}