{"record":{"id":"e5fce4d0ee9046e6","repo":"AlistGo/alist","slug":"stream-rangeread-did-not-get-all-data-in-peek-exp","errorCode":null,"errorMessage":"stream RangeRead did not get all data in peek, expect =%d ,actual =%d","messagePattern":"stream RangeRead did not get all data in peek, expect =(.+?) ,actual =(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/stream/stream.go","lineNumber":136,"sourceCode":"\t\thttpRange.Length = f.GetSize() - httpRange.Start\n\t}\n\tsize := httpRange.Start + httpRange.Length\n\tif f.peekBuff != nil && size <= int64(f.peekBuff.Len()) {\n\t\treturn io.NewSectionReader(f.peekBuff, httpRange.Start, httpRange.Length), nil\n\t}\n\tvar cache io.ReaderAt = f.GetFile()\n\tif cache == nil {\n\t\tif size <= InMemoryBufMaxSizeBytes {\n\t\t\tbufSize := min(size, f.GetSize())\n\t\t\t// 使用bytes.Buffer作为io.CopyBuffer的写入对象，CopyBuffer会调用Buffer.ReadFrom\n\t\t\t// 即使被写入的数据量与Buffer.Cap一致，Buffer也会扩大\n\t\t\tbuf := make([]byte, bufSize)\n\t\t\tn, err := io.ReadFull(f.Reader, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif n != int(bufSize) {\n\t\t\t\treturn nil, fmt.Errorf(\"stream RangeRead did not get all data in peek, expect =%d ,actual =%d\", bufSize, n)\n\t\t\t}\n\t\t\tf.peekBuff = bytes.NewReader(buf)\n\t\t\tf.Reader = io.MultiReader(f.peekBuff, f.Reader)\n\t\t\tcache = f.peekBuff\n\t\t} else {\n\t\t\tvar err error\n\t\t\tcache, err = f.CacheFullInTempFile()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\treturn io.NewSectionReader(cache, httpRange.Start, httpRange.Length), nil\n}\n\nvar _ model.FileStreamer = (*SeekableStream)(nil)\nvar _ model.FileStreamer = (*FileStream)(nil)\n","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/stream/stream.go#L118-L154","documentation":"In stream File.RangeRead, when no cached io.ReaderAt exists and size fits under InMemoryBufMaxSizeBytes, the head of the file is read into a peek buffer with io.ReadFull. This defensive check fires if ReadFull returns err==nil but fewer bytes than requested — a short read without error, which normal Readers should not produce.","triggerScenarios":"An underlying model.File stream (usually a link/HTTP or driver reader) that returns io.EOF early but is wrapped so ReadFull reports success with n < bufSize — essentially a misbehaving driver reader or a size/length mismatch between GetSize() and actual readable data.","commonSituations":"A storage driver reporting a wrong file size (metadata larger than real content), or a Reader wrapper that swallows an EOF short-read error.","solutions":["Verify the file's reported size matches the actual content size (re-stat the file on the remote)","Check the driver's Link/Reader implementation for a wrapper that hides ErrUnexpectedEOF","If the size is unreliable, use the temp-file caching path by exceeding InMemoryBufMaxSizeBytes or fix GetSize() in the driver"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"data, err := file.RangeRead(ctx, http_range.Range{Start: 0, Length: size})\nif err != nil {\n    if strings.Contains(err.Error(), \"did not get all data in peek\") {\n        // size metadata is wrong: re-stat the remote object and retry, or full-cache to temp file\n    }\n}","preventionTips":["Ensure driver GetSize() returns the true content length","Do not wrap readers in ways that swallow ErrUnexpectedEOF","Add driver unit tests asserting ReadFull(n) == n for declared sizes"],"tags":["go","stream","io","short-read","defensive-check"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}