From ed9eec0f20d3cf468cfbf96f2111e64d7192cb82 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 3 Jul 2026 15:37:09 +0100 Subject: [PATCH] fs/chunkedreader: fix spurious errors when a parallel stream is closed early Closing a stream in the parallel chunked reader cancels the stream's context, so the in-flight read returns context.Canceled. This was wrapped and returned as "failed to read stream", which the VFS cache downloader treats as a real download error - it only recognises asyncreader.ErrorStreamAbandoned as a benign teardown, as returned by the sequential reader. Return asyncreader.ErrorStreamAbandoned for a cancellation so tearing down the parallel reader (on close, seek or reposition) is recognised as benign, matching the sequential reader, instead of logging download errors and retrying when --vfs-read-chunk-streams is used with --vfs-cache-mode full. --- fs/chunkedreader/parallel.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/chunkedreader/parallel.go b/fs/chunkedreader/parallel.go index 15d0da4a1..a76d8915c 100644 --- a/fs/chunkedreader/parallel.go +++ b/fs/chunkedreader/parallel.go @@ -2,11 +2,13 @@ package chunkedreader import ( "context" + "errors" "fmt" "io" "sync" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/asyncreader" "github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/log" "github.com/rclone/rclone/fs/operations" @@ -136,6 +138,14 @@ func (s *stream) close() (err error) { if s.rc != nil { orErr(&err, s.rc.Close()) } + // close cancels the stream's context above, so a cancellation error + // just means the stream was intentionally abandoned. Report that as + // asyncreader.ErrorStreamAbandoned - the same signal the sequential + // reader uses - so callers (e.g. the VFS cache downloader) treat it as + // a benign teardown rather than a read failure. + if errors.Is(err, context.Canceled) { + return asyncreader.ErrorStreamAbandoned + } if err != nil && err != io.EOF { return fmt.Errorf("parallel chunked reader: failed to read stream at %d size %d: %w", s.offset, s.size, err) }