{"record":{"id":"ee012c3fbb0256bd","repo":"didi/DoKit","slug":"responsecode-connection-getresponsemessage","errorCode":null,"errorMessage":"responseCode + \" \" + connection.getResponseMessage()","messagePattern":"responseCode \\+ \" \" \\+ connection\\.getResponseMessage\\(\\)","errorType":"http","errorClass":"ResponseException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/UrlConnectionDownloader.java","lineNumber":96,"sourceCode":"          builder.append(\"no-cache\");\n        }\n        if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {\n          if (builder.length() > 0) {\n            builder.append(',');\n          }\n          builder.append(\"no-store\");\n        }\n\n        headerValue = builder.toString();\n      }\n\n      connection.setRequestProperty(\"Cache-Control\", headerValue);\n    }\n\n    int responseCode = connection.getResponseCode();\n    if (responseCode >= 300) {\n      connection.disconnect();\n      throw new ResponseException(responseCode + \" \" + connection.getResponseMessage(),\n          networkPolicy, responseCode);\n    }\n\n    long contentLength = connection.getHeaderFieldInt(\"Content-Length\", -1);\n    boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));\n\n    return new Response(connection.getInputStream(), fromCache, contentLength);\n  }\n\n  @Override public void shutdown() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && cache != null) {\n      ResponseCacheIcs.close(cache);\n    }\n  }\n\n  private static void installCacheIfNeeded(Context context) {\n    // DCL + volatile should be safe after Java 5.\n    if (cache == null) {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/UrlConnectionDownloader.java#L78-L114","documentation":"UrlConnectionDownloader.load() checks the HTTP response code and throws ResponseException(responseCode + \" \" + responseMessage, networkPolicy, responseCode) for any status >= 300. This is Picasso's signal that the image fetch failed at the HTTP level (redirect handling exhausted, 4xx auth/missing, 5xx server error); it is caught internally by BitmapHunter and converted into the error path (error drawable / onBitmapFailed) rather than crashing the app.","triggerScenarios":"Loading a URL that returns 404/410, a CDN link whose signed token expired (403), a server error (500/503), or a redirect loop (3xx not followed) — any responseCode >= 300 from the underlying HttpURLConnection.","commonSituations":"Expired or mis-signed image URLs; wrong Content-Type handled by a proxy; environment differences where a URL works locally but a staging/proxy returns 403; backend outage during image loads.","solutions":["Verify the URL in a browser or with curl -I to confirm the status code and fix the link or auth token.","Register an error fallback via .error(R.drawable.err) or implement Target/onBitmapFailed so the UI degrades gracefully.","For intermittent 5xx, allow retries (Picasso automatically retries failed requests up to its retry limit) and consider .networkPolicy(NetworkPolicy.NO_CACHE) to avoid caching a stale bad response path.","If 3xx appears, ensure the server sends Location properly — HttpURLConnection follows up to 5 HTTP redirects by default; a loop yields this exception."],"exampleFix":"// before\npicasso.load(expiredSignedUrl).into(imageView); // 403, blank view\n\n// after\npicasso.load(freshUrl)\n    .error(R.drawable.image_fallback)\n    .into(imageView, new Callback() {\n        @Override public void onError(Exception e) {\n            if (e instanceof ResponseException) {\n                int code = ((ResponseException) e).responseCode;\n                tracker.logImageFailure(url, code);\n            }\n        }\n        @Override public void onSuccess() {}\n    });","handlingStrategy":"try-catch","validationCode":"// Optional preflight — HEAD the URL to catch dead links early\n// (do this off the main thread; most apps instead rely on error callbacks)\nURL u = new URL(url);\nHttpURLConnection c = (HttpURLConnection) u.openConnection();\nc.setRequestMethod(\"HEAD\");\nboolean ok = c.getResponseCode() < 300;","typeGuard":null,"tryCatchPattern":"// ResponseException is delivered to Callback.onError / Target.onBitmapFailed — handle there\npicasso.load(url).error(R.drawable.fallback).into(imageView, new Callback.EmptyCallback() {\n    @Override public void onError(Exception e) {\n        if (e instanceof ResponseException) {\n            int code = ((ResponseException) e).responseCode;\n            log(\"image fetch failed HTTP \" + code);\n        }\n    }\n});","preventionTips":["Always attach .error(...) or an error callback for remote images.","Validate/refresh signed URLs before handing them to the image loader.","Monitor 4xx/5xx rates per endpoint to catch expired-CDN-token incidents early."],"tags":["android","picasso","network","http","image-loading"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}