{"record":{"id":"c243e9381cb5f511","repo":"didi/DoKit","slug":"request-transformer-requesttransformer-getclass","errorCode":null,"errorMessage":"Request transformer <requestTransformer.getClass().getCanonicalName()> returned null for <request>","messagePattern":"Request transformer <requestTransformer\\.getClass\\(\\)\\.getCanonicalName\\(\\)> returned null for <request>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/DokitPicasso.java","lineNumber":458,"sourceCode":"    cache.clear();\n    cleanupThread.shutdown();\n    stats.shutdown();\n    dispatcher.shutdown();\n    for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) {\n      deferredRequestCreator.cancel();\n    }\n    targetToDeferredRequestCreator.clear();\n    shutdown = true;\n  }\n\n  List<RequestHandler> getRequestHandlers() {\n    return requestHandlers;\n  }\n\n  Request transformRequest(Request request) {\n    Request transformed = requestTransformer.transformRequest(request);\n    if (transformed == null) {\n      throw new IllegalStateException(\"Request transformer \"\n          + requestTransformer.getClass().getCanonicalName()\n          + \" returned null for \"\n          + request);\n    }\n    return transformed;\n  }\n\n  void defer(ImageView view, DeferredRequestCreator request) {\n    targetToDeferredRequestCreator.put(view, request);\n  }\n\n  void enqueueAndSubmit(Action action) {\n    Object target = action.getTarget();\n    if (target != null && targetToAction.get(target) != action) {\n      // This will also check we are on the main thread.\n      cancelExistingRequest(target);\n      targetToAction.put(target, action);\n    }","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/DokitPicasso.java#L440-L476","documentation":"Every request passes through the configured RequestTransformer (identity by default) before hunting. transformRequest() enforces that the transformer returns a non-null Request; null triggers IllegalStateException naming the transformer class and the original request. The transform API is 'return a possibly-modified Request', never null — a null return would NPE later in the pipeline, so it is rejected at the boundary.","triggerScenarios":"A custom RequestTransformer (set via Builder.requestTransformer(...)) whose transformRequest returns null on a code path — e.g. when it does not recognize the URI, or when Kotlin nullable returns leak through; a transformer ported from a version that used null to signal 'skip'.","commonSituations":"Debug-only transformers that rewrite URLs and return null in release builds; conditional rewrite logic missing a default return input branch.","solutions":["Return the input request from transformRequest when no rewrite applies","Audit every return path of the transformer named in the message","In Kotlin, declare the return type as non-nullable Request"],"exampleFix":"// before\nnew DokitPicasso.Builder(context)\n  .requestTransformer(new RequestTransformer() {\n    @Override public Request transformRequest(Request request) {\n      if (request.uri != null && request.uri.getHost() != null) return request;\n      return null; // -> IllegalStateException\n    }\n  }).build();\n\n// after\n@Override public Request transformRequest(Request request) {\n  return request.buildUpon().build(); // always non-null; modify only when needed\n}","handlingStrategy":"validation","validationCode":"// Ensure every branch returns non-null; default to the input request:\nRequestTransformer safe = new RequestTransformer() {\n  @Override public Request transformRequest(Request request) {\n    if (request.uri == null) return request;\n    if (!request.uri.isRelative()) return request;\n    return request.buildUpon().uri(Uri.parse(\"https://cdn.example.com/\" + request.uri)).build();\n  }\n};","typeGuard":null,"tryCatchPattern":"// transformRequest runs synchronously inside submit(), so a caller-side guard works:\n// just guarantee non-null in the transformer. If wrapping third-party transformers:\nRequest transformSafe(Request in, RequestTransformer t) {\n  Request out = t.transformRequest(in);\n  return out != null ? out : in;\n}","preventionTips":["transformRequest must return a Request on every path — 'no change' means return the input","Kotlin: declare override fun transformRequest(request: Request): Request (non-nullable)","Unit-test the transformer with http, https, file, and resource-id requests"],"tags":["android","picasso","request-transformer","null-safety"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}