diff --git a/object_store/src/aws/client.rs b/object_store/src/aws/client.rs index e06a0ce1846b..fed6911e6f04 100644 --- a/object_store/src/aws/client.rs +++ b/object_store/src/aws/client.rs @@ -538,6 +538,16 @@ impl S3Client { upload_id: &str, parts: Vec, ) -> Result { + let parts = if parts.is_empty() { + // If no parts were uploaded, upload an empty part + // otherwise the completion request will fail + let part = self + .put_part(location, &upload_id.to_string(), 0, Bytes::new()) + .await?; + vec![part] + } else { + parts + }; let request = CompleteMultipartUpload::from(parts); let body = quick_xml::se::to_string(&request).unwrap(); diff --git a/object_store/src/lib.rs b/object_store/src/lib.rs index deb133d250a6..af5676ef5003 100644 --- a/object_store/src/lib.rs +++ b/object_store/src/lib.rs @@ -2200,6 +2200,21 @@ mod tests { let meta = storage.head(&path).await.unwrap(); assert_eq!(meta.size, chunk_size * 2); + + // Empty case + let path = Path::from("test_empty_multipart"); + + let id = multipart.create_multipart(&path).await.unwrap(); + + let parts = vec![]; + + multipart + .complete_multipart(&path, &id, parts) + .await + .unwrap(); + + let meta = storage.head(&path).await.unwrap(); + assert_eq!(meta.size, 0); } #[cfg(any(feature = "azure", feature = "aws"))]