diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index 3a3e64763..e8a1fd064 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -377,9 +377,18 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque } } - err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + // Delete bucket directory first, then collection. This order ensures that if + // collection deletion fails, the bucket directory is already gone, preventing + // the "collection exists but bucket directory missing" inconsistency that blocks + // bucket recreation. An orphaned collection is harmless and will be cleaned up + // or reused when the bucket is recreated. + err := s3a.rm(s3a.option.BucketsPath, bucket, false, true) + if err != nil { + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } - // delete collection + err = s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{ Collection: s3a.getCollectionName(bucket), } @@ -393,15 +402,9 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque }) if err != nil { - s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) - return - } - - err = s3a.rm(s3a.option.BucketsPath, bucket, false, true) - - if err != nil { - s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) - return + // Log but don't fail — the bucket directory is already removed, so the bucket + // is effectively deleted. The orphaned collection will be cleaned up or reused. + glog.Errorf("DeleteBucketHandler: failed to delete collection for bucket %s: %v", bucket, err) } // Clean up bucket-related caches, locks, and metrics after successful deletion