lingochamp/FileDownloader · error · FileDownloadGiveUpRetryException

fetched length[ ] != content length[ ], range[ , ) offset[…

Error message

fetched length[%d] != content length[%d], range[%d, %d) offset[%d] fetch begin offset[%d]

What it means

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.

Solutions

  1. 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
  2. Check for and bypass intermediary proxies/CDN/compression middleware that may alter the body or Range handling
  3. Ensure the server supports HTTP Range requests correctly (responds 206 with exact requested byte range)
  4. Update the filedownloader library version; some edge cases around range/offset bookkeeping were fixed over time
  5. If it only happens on flaky networks, pre-check connectivity and re-issue the download; this exception intentionally disables retry for this attempt

Example fix

// before: single attempt fails hard on flaky network
FileDownloader.getImpl().create(url).setPath(path).start();
// after: catch and re-attempt via listener
FileDownloader.getImpl().create(url).setPath(path)
    .setListener(new FileDownloadListener() {
        @Override public void warn(FileDownloadBase base, FileDownloadSoFar sofar) {}
        @Override public void started(...) {}
        // on error, check if bytes fetched mismatch and re-create the task
    }).start();
Defensive patterns

Strategy: retry

Validate before calling

// before starting the download, probe the server's range support
HttpURLConnection c = (HttpURLConnection) new URL(url).openConnection();
c.setRequestProperty("Range", "bytes=0-");
if (c.getResponseCode() != 206) {
    throw new IllegalStateException("Server does not honor Range; partial download will fail");
}
long declared = c.getContentLength();
if (declared <= 0) {
    throw new IllegalStateException("Server reports no usable Content-Length for range request");
}

Try / catch

try {
    task.start();
} catch (FileDownloadGiveUpRetryException e) {
    // fetched bytes != content-length: server inconsistency
    log.warn("Body length mismatch, re-issuing download", e);
    FileDownloader.getImpl().create(url).setPath(path)
        .setListener(listener).start(); // fresh attempt re-syncs offsets
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of lingochamp/FileDownloader@6237a8cac1 (2026-09-08). Data as JSON: /api/errors/9e9868dc1e1ae065. Report an issue: GitHub.

Appendix: source

Thrown at library/src/main/java/com/liulishuo/filedownloader/download/FetchDataTask.java:190

            }

            try {
                if (outputStream != null) sync();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        final long fetchedLength = currentOffset - fetchBeginOffset;
        if (contentLength != TOTAL_VALUE_IN_CHUNKED_RESOURCE && contentLength != fetchedLength) {
            throw new FileDownloadGiveUpRetryException(
                    FileDownloadUtils.formatString("fetched length[%d] != content length[%d],"
                                    + " range[%d, %d) offset[%d] fetch begin offset[%d]",
                            fetchedLength, contentLength,
                            startOffset, endOffset, currentOffset, fetchBeginOffset));
        }

        // callback completed
        callback.onCompleted(hostRunnable, startOffset, endOffset);
    }

    private final FileDownloadDatabase database;
    private volatile long lastSyncBytes = 0;
    private volatile long lastSyncTimestamp = 0;

    private void checkAndSync() {
        final long now = SystemClock.elapsedRealtime();
        final long bytesDelta = currentOffset - lastSyncBytes;
        final long timestampDelta = now - lastSyncTimestamp;

View on GitHub (pinned to 6237a8cac1)