From 60eeb30b7225afeb1f7ce98c015b77c36c525614 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:42:50 +0000 Subject: [PATCH 1/3] Initial plan From 4c8e3e1b9a4d2e1828a88047c9e3ba1452341659 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:47:23 +0000 Subject: [PATCH 2/3] Fix multipart upload race condition and ETag normalization - Add strings import for ETag processing - Normalize ETag values by trimming quotes from S3 responses - Add debug logging when appending parts - Add pre-completion validation logging showing part count and ETags - Mutex protection already in place for parts slice append Co-authored-by: bwalsh <47808+bwalsh@users.noreply.github.com> --- gen3-client/g3cmd/upload-multipart.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gen3-client/g3cmd/upload-multipart.go b/gen3-client/g3cmd/upload-multipart.go index f1dc1e6..789ac78 100644 --- a/gen3-client/g3cmd/upload-multipart.go +++ b/gen3-client/g3cmd/upload-multipart.go @@ -10,6 +10,7 @@ import ( "os" "sort" "strconv" + "strings" "sync" "time" @@ -130,6 +131,8 @@ func multipartUpload(g3 Gen3Interface, fileInfo FileInfo, retryCount int, bucket err = errors.New("No ETag found in header") return } + // Normalize ETag by trimming quotes (S3 returns quoted ETags) + eTag = strings.Trim(eTag, `"`) return }) if err != nil { @@ -140,6 +143,7 @@ func multipartUpload(g3 Gen3Interface, fileInfo FileInfo, retryCount int, bucket multipartUploadLock.Lock() // to avoid racing conditions parts = append(parts, (MultipartPartObject{PartNumber: chunkIndex, ETag: eTag})) + log.Printf("Appended part %d with ETag %s\n", chunkIndex, eTag) bar.Add(n) multipartUploadLock.Unlock() } @@ -165,6 +169,12 @@ func multipartUpload(g3 Gen3Interface, fileInfo FileInfo, retryCount int, bucket return parts[i].PartNumber < parts[j].PartNumber // sort parts in ascending order }) + // Log part details before completing multipart upload for debugging + log.Printf("Completing multipart upload with %d parts for file %s\n", len(parts), fileInfo.Filename) + for _, part := range parts { + log.Printf(" Part %d: ETag=%s\n", part.PartNumber, part.ETag) + } + if err = CompleteMultipartUpload(g3, key, uploadID, parts, bucketName); err != nil { logs.AddToFailedLog(fileInfo.FilePath, fileInfo.Filename, fileInfo.FileMetadata, guid, retryCount, true, true) err = fmt.Errorf("FAILED multipart upload for %s: %s", fileInfo.Filename, err.Error()) From 7452cd71c5c3502e96a74518d3ae25e5306eb937 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:50:51 +0000 Subject: [PATCH 3/3] Update .gitignore to exclude build artifacts Co-authored-by: bwalsh <47808+bwalsh@users.noreply.github.com> --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index a20f5d6..e8309e0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,8 @@ /vendor/ /Godeps/ +# Build artifacts +/build/ +checksums.txt + # End of https://www.toptal.com/developers/gitignore/api/go