{"record":{"id":"941a8fadc5b263f0","repo":"apache/hadoop","slug":"this-operation-is-not-supported-across-two-differe-941a8f","errorCode":null,"errorMessage":"This operation is not supported across two different storage classes.","messagePattern":"This operation is not supported across two different storage classes\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorage.java","lineNumber":844,"sourceCode":"        if (!srcBucketInfo.exists()) {\n          throw new FileNotFoundException(\"Bucket not found: \" + srcBucketName);\n        }\n\n        StorageResourceId dstBucketResourceId = new StorageResourceId(dstBucketName);\n        GoogleCloudStorageItemInfo dstBucketInfo =\n            getGoogleCloudStorageItemInfo(gcsImpl, bucketInfoCache, dstBucketResourceId);\n        if (!dstBucketInfo.exists()) {\n          throw new FileNotFoundException(\"Bucket not found: \" + dstBucketName);\n        }\n\n        // TODO: Restrict this only when copy-with-rewrite is enabled\n        if (!srcBucketInfo.getLocation().equals(dstBucketInfo.getLocation())) {\n          throw new UnsupportedOperationException(\n              \"This operation is not supported across two different storage locations.\");\n        }\n\n        if (!srcBucketInfo.getStorageClass().equals(dstBucketInfo.getStorageClass())) {\n          throw new UnsupportedOperationException(\n              \"This operation is not supported across two different storage classes.\");\n        }\n      }\n      checkArgument(\n          !isNullOrEmpty(source.getObjectName()), \"srcObjectName must not be null or empty\");\n      checkArgument(\n          !isNullOrEmpty(destination.getObjectName()), \"dstObjectName must not be null or empty\");\n      if (srcBucketName.equals(dstBucketName)\n          && source.getObjectName().equals(destination.getObjectName())) {\n        throw new IllegalArgumentException(\n            String.format(\n                \"Copy destination must be different from source for %s.\",\n                StringPaths.fromComponents(srcBucketName, source.getObjectName())));\n      }\n    }\n  }\n\n  private static GoogleCloudStorageItemInfo getGoogleCloudStorageItemInfo(","sourceCodeStart":826,"sourceCodeEnd":862,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorage.java#L826-L862","documentation":"Thrown by GoogleCloudStorage.copy when the source and destination buckets have different GCS storage classes (STANDARD, NEARLINE, COLDLINE, ARCHIVE). The connector fetches both buckets' metadata (via a bucketInfoCache) and refuses the copy before it starts; a source comment notes the check is broader than necessary and should apply only when copy-with-rewrite is enabled. An equivalent check for differing bucket locations throws just above it.","triggerScenarios":"Calling gcs.copy(source, destination) where srcBucketInfo.getStorageClass() != dstBucketInfo.getStorageClass() — e.g. STANDARD -> COLDLINE/ARCHIVE. Both buckets must exist (a missing bucket throws FileNotFoundException earlier), and src/dst object names must be non-empty or checkArgument fires first.","commonSituations":"Archival pipelines copying hot data into cheaper-class buckets; cross-project backups where the destination bucket was created with a different default storage class; bucket class migrations; distcp or GHFS rename workflows that route through GoogleCloudStorage.copy across buckets.","solutions":["Make source and destination bucket storage classes match (recreate or transition the destination bucket, or pick a same-class destination).","Keep the copy within one bucket and change the object's storage class with a lifecycle rule or `gcloud storage objects update` / gsutil rewrite, which use the Rewrite API that handles class changes.","Use Storage Transfer Service or `gcloud storage cp` for the cross-class transfer instead of the connector's copy API.","If you maintain connector code, narrow the check to copy-with-rewrite mode (per the TODO) only after testing rewrite behavior across classes."],"exampleFix":"// before\ngcs.copy(\n    new StorageResourceId(\"hot-bkt\", \"data/file1\"),\n    new StorageResourceId(\"archive-bkt\", \"data/file1\")); // throws: class mismatch\n\n// after: same-class destination bucket, or rewrite in place\n// gcloud storage objects update gs://hot-bkt/data/file1 --storage-class=COLDLINE\n// (or) gsutil -s COLDLINE cp -r gs://hot-bkt gs://archive-bkt","handlingStrategy":"validation","validationCode":"// Compare both bucket metadata before copying\nGoogleCloudStorageItemInfo srcB = gcs.getItemInfo(new StorageResourceId(srcBucket));\nGoogleCloudStorageItemInfo dstB = gcs.getItemInfo(new StorageResourceId(dstBucket));\ncheckState(srcB.exists() && dstB.exists(), \"both buckets must exist\");\ncheckState(Objects.equals(srcB.getLocation(), dstB.getLocation()), \"location mismatch\");\ncheckState(Objects.equals(srcB.getStorageClass(), dstB.getStorageClass()), \"storage class mismatch\");\ngcs.copy(srcId, dstId);","typeGuard":null,"tryCatchPattern":"try { gcs.copy(srcId, dstId); } catch (UnsupportedOperationException e) { if (e.getMessage().contains(\"storage classes\") || e.getMessage().contains(\"storage locations\")) { /* fall back to gcloud rewrite / transfer, or same-class bucket */ } else throw e; }","preventionTips":["Standardize storage class and location for buckets used as copy/rename sources and destinations.","Encode the location/class constraint in bucket-creation templates so new buckets match.","For archival to cold classes, plan lifecycle rules or rewrite-based transfer instead of connector copy."],"tags":["gcs","copy","storage-class","cross-bucket","unsupported-operation"],"backgroundTag":"cross-bucket-copy-storage-class-mismatch","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}