From 7c0e6eaaec145dd47d0ecea7556a3a2786bc9f1d Mon Sep 17 00:00:00 2001 From: overtrue Date: Wed, 29 Apr 2026 17:08:13 +0800 Subject: [PATCH] fix(s3): preserve delete bucket conflict codes --- crates/s3/src/client.rs | 52 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/s3/src/client.rs b/crates/s3/src/client.rs index 9765f0b..cd05b76 100644 --- a/crates/s3/src/client.rs +++ b/crates/s3/src/client.rs @@ -1736,9 +1736,11 @@ impl ObjectStore for S3Client { .send() .await .map_err(|e| { - let err_str = e.to_string(); + let err_str = Self::format_sdk_error(&e); if err_str.contains("NotFound") || err_str.contains("NoSuchBucket") { Error::NotFound(format!("Bucket not found: {bucket}")) + } else if err_str.contains("BucketNotEmpty") { + Error::Conflict(err_str) } else { Error::Network(err_str) } @@ -3528,6 +3530,54 @@ mod tests { } } + #[tokio::test] + async fn delete_bucket_maps_bucket_not_empty_to_conflict() { + let response = http::Response::builder() + .status(409) + .header("x-amz-error-code", "BucketNotEmpty") + .body(SdkBody::from( + r#" + + BucketNotEmpty + The bucket you tried to delete is not empty. +"#, + )) + .expect("build delete bucket response"); + let (client, _request_receiver) = test_s3_client(Some(response)); + + let result = client.delete_bucket("bucket").await; + + match result { + Err(Error::Conflict(message)) => assert!(message.contains("BucketNotEmpty")), + other => panic!("Expected Conflict for non-empty bucket, got: {other:?}"), + } + } + + #[tokio::test] + async fn delete_bucket_maps_missing_bucket_to_not_found() { + let response = http::Response::builder() + .status(404) + .header("x-amz-error-code", "NoSuchBucket") + .body(SdkBody::from( + r#" + + NoSuchBucket + The specified bucket does not exist. +"#, + )) + .expect("build missing bucket response"); + let (client, _request_receiver) = test_s3_client(Some(response)); + + let result = client.delete_bucket("missing-bucket").await; + + match result { + Err(Error::NotFound(message)) => { + assert_eq!(message, "Bucket not found: missing-bucket") + } + other => panic!("Expected NotFound for missing bucket, got: {other:?}"), + } + } + #[tokio::test] async fn delete_objects_with_force_delete_sets_rustfs_header() { let response = http::Response::builder()