{"record":{"id":"9e9868dc1e1ae065","repo":"lingochamp/FileDownloader","slug":"fetched-length-d-content-length-d-range-d","errorCode":null,"errorMessage":"fetched length[%d] != content length[%d], range[%d, %d) offset[%d] fetch begin offset[%d]","messagePattern":"fetched length\\[(.+?)\\] != content length\\[(.+?)\\], range\\[(.+?), (.+?)\\) offset\\[(.+?)\\] fetch begin offset\\[(.+?)\\]","errorType":"exception","errorClass":"FileDownloadGiveUpRetryException","httpStatus":null,"severity":"error","filePath":"library/src/main/java/com/liulishuo/filedownloader/download/FetchDataTask.java","lineNumber":190,"sourceCode":"            }\n\n            try {\n                if (outputStream != null) sync();\n            } finally {\n                if (outputStream != null) {\n                    try {\n                        outputStream.close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n\n        }\n\n        final long fetchedLength = currentOffset - fetchBeginOffset;\n        if (contentLength != TOTAL_VALUE_IN_CHUNKED_RESOURCE && contentLength != fetchedLength) {\n            throw new FileDownloadGiveUpRetryException(\n                    FileDownloadUtils.formatString(\"fetched length[%d] != content length[%d],\"\n                                    + \" range[%d, %d) offset[%d] fetch begin offset[%d]\",\n                            fetchedLength, contentLength,\n                            startOffset, endOffset, currentOffset, fetchBeginOffset));\n        }\n\n        // callback completed\n        callback.onCompleted(hostRunnable, startOffset, endOffset);\n    }\n\n    private final FileDownloadDatabase database;\n    private volatile long lastSyncBytes = 0;\n    private volatile long lastSyncTimestamp = 0;\n\n    private void checkAndSync() {\n        final long now = SystemClock.elapsedRealtime();\n        final long bytesDelta = currentOffset - lastSyncBytes;\n        final long timestampDelta = now - lastSyncTimestamp;","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/lingochamp/FileDownloader/blob/6237a8cac174bcc916e4342b14ab1ab72a5768d4/library/src/main/java/com/liulishuo/filedownloader/download/FetchDataTask.java#L172-L208","documentation":"FileDownloadGiveUpRetryException thrown by FetchDataTask.run() after a download chunk finishes: the number of bytes actually fetched does not equal the Content-Length advertised by the server (for non-chunked transfers). The library treats this as a server/range inconsistency it cannot recover from, so it aborts rather than retrying.","triggerScenarios":"The server's response body length differs from the declared Content-Length for the requested Range [startOffset, endOffset); e.g. server truncates the body, closes the connection early, misreports Content-Length, or returns fewer bytes than promised for a resumed range request.","commonSituations":"Flaky proxies or CDNs cutting connections mid-body; misconfigured servers behind load balancers that strip/alter Range headers; servers that ignore Range and return a different body than expected; unstable mobile networks that end the stream without an error; buggy server-side compression middleware changing body size.","solutions":["Retry the download; if it fails on the same file repeatedly, the server is serving inconsistent Content-Length — verify with curl -r or wget against the URL","Check for and bypass intermediary proxies/CDN/compression middleware that may alter the body or Range handling","Ensure the server supports HTTP Range requests correctly (responds 206 with exact requested byte range)","Update the filedownloader library version; some edge cases around range/offset bookkeeping were fixed over time","If it only happens on flaky networks, pre-check connectivity and re-issue the download; this exception intentionally disables retry for this attempt"],"exampleFix":"// before: single attempt fails hard on flaky network\nFileDownloader.getImpl().create(url).setPath(path).start();\n// after: catch and re-attempt via listener\nFileDownloader.getImpl().create(url).setPath(path)\n    .setListener(new FileDownloadListener() {\n        @Override public void warn(FileDownloadBase base, FileDownloadSoFar sofar) {}\n        @Override public void started(...) {}\n        // on error, check if bytes fetched mismatch and re-create the task\n    }).start();","handlingStrategy":"retry","validationCode":"// before starting the download, probe the server's range support\nHttpURLConnection c = (HttpURLConnection) new URL(url).openConnection();\nc.setRequestProperty(\"Range\", \"bytes=0-\");\nif (c.getResponseCode() != 206) {\n    throw new IllegalStateException(\"Server does not honor Range; partial download will fail\");\n}\nlong declared = c.getContentLength();\nif (declared <= 0) {\n    throw new IllegalStateException(\"Server reports no usable Content-Length for range request\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    task.start();\n} catch (FileDownloadGiveUpRetryException e) {\n    // fetched bytes != content-length: server inconsistency\n    log.warn(\"Body length mismatch, re-issuing download\", e);\n    FileDownloader.getImpl().create(url).setPath(path)\n        .setListener(listener).start(); // fresh attempt re-syncs offsets\n}","preventionTips":["Verify the URL returns stable Content-Length and correct 206 responses for Range requests (curl -r 0-99 -I)","Bypass proxies/CDN/gzip middleware that can alter body size between HEAD and GET","Set a path consistent with prior downloads so offsets stay in sync, or clear stale temp files after version changes","Keep the filedownloader library up to date; range bookkeeping bugs were fixed in later versions"],"tags":["android","filedownloader","http","range-request","content-length"],"backgroundTag":"http-request-failed","analyzedSha":"6237a8cac174bcc916e4342b14ab1ab72a5768d4","analyzedAt":"2026-09-08T23:50:48.168Z","contentChangedAt":"2026-09-08T23:50:48.168Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}