From 4dcd33bbc865a40e37d1e6efb03af68945bc0852 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 15 Dec 2025 18:43:40 -0800 Subject: [PATCH] fix: handle missing idx file for empty volumes during copy (#7777) (#7778) When copying/evacuating empty volumes, the .idx file may not exist on disk (this is allowed by checkIdxFile for volumes with only super block in .dat). This fix: 1. Uses os.IsNotExist() instead of err == os.ErrNotExist for proper wrapped error checking in CopyFile 2. Treats missing source file as success when StopOffset == 0 (empty file) 3. Allows checkCopyFiles to pass when idx file doesn't exist but IdxFileSize == 0 (empty volume) Fixes volumeServer.evacuate and volume.fix.replication for empty volumes. --- weed/server/volume_grpc_copy.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/weed/server/volume_grpc_copy.go b/weed/server/volume_grpc_copy.go index a2b5acd81..80c32d73e 100644 --- a/weed/server/volume_grpc_copy.go +++ b/weed/server/volume_grpc_copy.go @@ -234,9 +234,13 @@ todo: maybe should check the received count and deleted count of the volume func checkCopyFiles(originFileInf *volume_server_pb.ReadVolumeFileStatusResponse, hasRemoteDatFile bool, idxFileName, datFileName string) error { stat, err := os.Stat(idxFileName) if err != nil { - return fmt.Errorf("stat idx file %s failed: %v", idxFileName, err) - } - if originFileInf.IdxFileSize != uint64(stat.Size()) { + // If the idx file doesn't exist but the expected size is 0, that's OK (empty volume) + if os.IsNotExist(err) && originFileInf.IdxFileSize == 0 { + // empty volume, idx file not needed + } else { + return fmt.Errorf("stat idx file %s failed: %v", idxFileName, err) + } + } else if originFileInf.IdxFileSize != uint64(stat.Size()) { return fmt.Errorf("idx file %s size [%v] is not same as origin file size [%v]", idxFileName, stat.Size(), originFileInf.IdxFileSize) } @@ -373,8 +377,12 @@ func (vs *VolumeServer) CopyFile(req *volume_server_pb.CopyFileRequest, stream v file, err := os.Open(fileName) if err != nil { - if req.IgnoreSourceFileNotFound && err == os.ErrNotExist { - return nil + if os.IsNotExist(err) { + // If file doesn't exist and we're asked to copy 0 bytes (empty file), + // or if IgnoreSourceFileNotFound is set, treat as success + if req.IgnoreSourceFileNotFound || req.StopOffset == 0 { + return nil + } } return err }