{"record":{"id":"6f6a416d9563c205","repo":"AlistGo/alist","slug":"chunk-download-size-incorrect-expected-d-got-d","errorCode":null,"errorMessage":"chunk download size incorrect, expected=%d, got=%d","messagePattern":"chunk download size incorrect, expected=(.+?), got=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/net/request.go","lineNumber":462,"sourceCode":"\t\t}\n\t\treturn 0, errOverloadRetry\n\t}\n\tdefer resp.Body.Close()\n\t//only check file size on the first task\n\tif ch.id == 0 {\n\t\terr = d.checkTotalBytes(resp)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\td.sendChunkTask(true)\n\tn, err := utils.CopyWithBuffer(ch.buf, resp.Body)\n\n\tif err != nil {\n\t\treturn n, &errNeedRetry{err: err}\n\t}\n\tif n != ch.size {\n\t\terr = fmt.Errorf(\"chunk download size incorrect, expected=%d, got=%d\", ch.size, n)\n\t\treturn n, &errNeedRetry{err: err}\n\t}\n\n\treturn n, nil\n}\nfunc (d *downloader) getParamsFromChunk(ch *chunk) *HttpRequestParams {\n\tvar params HttpRequestParams\n\tawsutil.Copy(&params, d.params)\n\n\t// Get the getBuf byte range of data\n\tparams.Range = http_range.Range{Start: ch.start, Length: ch.size}\n\treturn &params\n}\n\nfunc (d *downloader) checkTotalBytes(resp *http.Response) error {\n\tvar err error\n\ttotalBytes := int64(-1)\n\tcontentRange := resp.Header.Get(\"Content-Range\")","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/net/request.go#L444-L480","documentation":"After copying a chunk body, the downloader verifies the byte count matches the chunk's expected size; a mismatch is wrapped in errNeedRetry so the chunk (or the request) is retried. Causes include premature connection closes, servers ignoring Range and sending truncated data, or proxies cutting the transfer.","triggerScenarios":"io.CopyN-style copy of resp.Body returns fewer bytes than ch.size (short read); server closes the connection mid-chunk; server mishandles the Range header so the body length differs from the requested range length.","commonSituations":"Flaky networks or mobile connections; remote servers with broken Range support; content changed size between the initial HEAD/probe and the ranged GET (file replaced upstream); interception by proxies that truncate bodies.","solutions":["Let the built-in retry (errNeedRetry) run first — transient short reads usually recover.","Refresh the cached file metadata (size) if the upstream file may have changed; the size mismatch with totalBytes is the sibling symptom.","If the server has broken Range support, avoid chunked download for that storage or fix the server.","Stabilize the network path (disable aggressive proxying, check MTU/VPN)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"var retryable *errNeedRetry\nif errors.As(err, &retryable) { /* chunk can be re-fetched */ }","tryCatchPattern":"if e, ok := err.(*errNeedRetry); ok && strings.Contains(e.Error(), \"chunk download size incorrect\") {\n    // re-probe size, then re-download the chunk with fresh range\n}","preventionTips":["Re-fetch file metadata when short-read errors cluster on one file (upstream size changed).","Verify servers you integrate actually honor Range before enabling chunked download.","Let errNeedRetry retries exhaust before escalating — most short reads are transient."],"tags":["network","download","integrity","retry","http-range","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}