{"record":{"id":"d4962f6965be77c2","repo":"didi/DoKit","slug":"transformation-transformation-key-return-d4962f","errorCode":null,"errorMessage":"Transformation \" + transformation.key() + \" returned input Bitmap but recycled it.","messagePattern":"Transformation \" \\+ transformation\\.key\\(\\) \\+ \" returned input Bitmap but recycled it\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java","lineNumber":461,"sourceCode":"            .append(transformation.key())\n            .append(\" returned null after \")\n            .append(i)\n            .append(\" previous transformation(s).\\n\\nTransformation list:\\n\");\n        for (Transformation t : transformations) {\n          builder.append(t.key()).append('\\n');\n        }\n        DokitPicasso.HANDLER.post(new Runnable() {\n          @Override public void run() {\n            throw new NullPointerException(builder.toString());\n          }\n        });\n        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      }","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java#L443-L479","documentation":"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.","triggerScenarios":"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.","commonSituations":"Over-aggressive memory optimization copying the 'recycle what you allocate' pattern onto the input bitmap; merging code from two transformations with different recycle conventions.","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"],"exampleFix":"// before\n@Override public Bitmap transform(Bitmap src) {\n  Bitmap out = Bitmap.createScaledBitmap(src, 100, 100, true);\n  src.recycle();\n  return src; // recycled input returned -> IllegalStateException\n}\n\n// after\n@Override public Bitmap transform(Bitmap src) {\n  Bitmap out = Bitmap.createScaledBitmap(src, 100, 100, true);\n  if (out != src) src.recycle();\n  return out; // return the new bitmap\n}","handlingStrategy":"validation","validationCode":"// Canonical safe pattern: return the input un-recycled, or a new bitmap with input recycled.\nstatic Bitmap finishTransform(Bitmap src, Bitmap out) {\n  if (out == src && src.isRecycled()) throw new AssertionError(\"input recycled\"); // catch in dev\n  return out;\n}","typeGuard":null,"tryCatchPattern":"// Posted to the main thread by Picasso; cannot be caught around .into().\n// Fix the transformation source:\n// WRONG: src.recycle(); return src;\n// RIGHT: Bitmap out = ...; if (out != src) src.recycle(); return out;","preventionTips":["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"],"tags":["android","picasso","bitmap","recycle","transformation"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}