{"record":{"id":"f2c123eecc2457a3","repo":"didi/DoKit","slug":"transformation-transformation-key-return","errorCode":null,"errorMessage":"Transformation \" + transformation.key() + \" returned null after \" + i + \" previous transformation(s).\\n\\nTransformation list:\\n\" + ...","messagePattern":"Transformation \" \\+ transformation\\.key\\(\\) \\+ \" returned null after \" \\+ i \\+ \" previous transformation\\(s\\)\\.\\\\n\\\\nTransformation list:\\\\n\" \\+ \\.\\.\\.","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java","lineNumber":452,"sourceCode":"                \"Transformation \" + transformation.key() + \" crashed with exception.\", e);\n          }\n        });\n        return null;\n      }\n\n      if (newResult == null) {\n        final StringBuilder builder = new StringBuilder() //\n            .append(\"Transformation \")\n            .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()) {","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/BitmapHunter.java#L434-L470","documentation":"applyCustomTransformations requires every Transformation.transform() to return a non-null Bitmap. If one returns null, Picasso posts a NullPointerException whose message lists the failing transformation's key, its position in the chain, and the full transformation list, then aborts the request. The detailed message exists precisely to identify which of several chained transformations misbehaved.","triggerScenarios":"A transform whose code path returns null (e.g. early-return on invalid input, third-party SDK returning null); chaining transformations where an earlier one recycles the bitmap and a later one returns null after createBitmap fails; Kotlin transform with an implicit null from a nullable helper.","commonSituations":"Defensive 'return null on bad input' coding style inside transformations; copy-pasted transformations from code that assumed Picasso tolerates null.","solutions":["Match the message's key to your Transformation class and make it return the input bitmap unchanged instead of null","Treat the contract as: transform must always return a usable Bitmap — validate before mutating, never return null","If you cannot produce output, return src (identity) so the request completes","Check chained transformations individually to find the null-returner named in the message"],"exampleFix":"// before\n@Override public Bitmap transform(Bitmap src) {\n  if (src.getWidth() < 10) return null;\n  return scale(src);\n}\n\n// after\n@Override public Bitmap transform(Bitmap src) {\n  if (src.getWidth() < 10 || src.getHeight() < 10) return src; // identity, never null\n  return scale(src);\n}","handlingStrategy":"validation","validationCode":"// Enforce the contract in a decorator:\nfinal class NonNullTransformation implements Transformation {\n  private final Transformation delegate;\n  NonNullTransformation(Transformation d) { this.delegate = d; }\n  @Override public Bitmap transform(Bitmap src) {\n    Bitmap out = delegate.transform(src);\n    return out != null ? out : src; // never null\n  }\n  @Override public String key() { return delegate.key() + \"!nonnull\"; }\n}","typeGuard":null,"tryCatchPattern":"// Thrown on the main thread by Picasso, not at the load call site — catch inside the\n// transformation instead:\n@Override public Bitmap transform(Bitmap src) {\n  try {\n    Bitmap out = doScale(src);\n    return out != null ? out : src;\n  } catch (RuntimeException e) {\n    return src;\n  }\n}","preventionTips":["Treat 'transform never returns null' as a hard API contract; return src as identity fallback","In Kotlin, declare transform(src: Bitmap): Bitmap (non-nullable) so the compiler enforces it","Test each transformation standalone with tiny and huge bitmaps"],"tags":["android","picasso","bitmap","transformation"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}