{"record":{"id":"b632dba92b271e49","repo":"apache/hadoop","slug":"copy-destination-must-be-different-from-source-for","errorCode":null,"errorMessage":"Copy destination must be different from source for %s.","messagePattern":"Copy destination must be different from source for (.+?)\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorage.java","lineNumber":854,"sourceCode":"\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(\n      GoogleCloudStorage gcsImpl,\n      Map<StorageResourceId, GoogleCloudStorageItemInfo> bucketInfoCache,\n      StorageResourceId resourceId)\n      throws IOException {\n    GoogleCloudStorageItemInfo storageItemInfo = bucketInfoCache.get(resourceId);\n    if (storageItemInfo != null) {\n      return storageItemInfo;\n    }\n    storageItemInfo = gcsImpl.getItemInfo(resourceId);\n    bucketInfoCache.put(resourceId, storageItemInfo);","sourceCodeStart":836,"sourceCodeEnd":872,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorage.java#L836-L872","documentation":"GoogleCloudStorage.copy throws IllegalArgumentException when source and destination resolve to the identical bucket AND identical object name. The connector treats a self-copy as a caller bug rather than a no-op or a way to mint a new generation. The check runs after the bucket location/storage-class checks and the non-empty object-name checkArguments.","triggerScenarios":"gcs.copy(source, destination) where srcBucketName.equals(dstBucketName) && source.getObjectName().equals(destination.getObjectName()) — e.g. rename fallbacks that compute dst from src, dedup logic, or scripts that pass the same path twice.","commonSituations":"Templated jobs where destination is derived from the source string and ends up identical; 'copy to backup' scripts invoked with the same src/dst arguments; rename implementations that fall back to copy without an equality guard.","solutions":["Guard the call: skip copy when src and dst bucket+object names are equal (usually a no-op for the caller's intent).","Fix the code constructing the destination so it can never equal the source.","If you genuinely need a fresh generation of the same object, copy to a temporary name then copy/delete back, or call the storage client's CopyRequest directly."],"exampleFix":"// before\ngcs.copy(srcId, dstId); // dstId.equals(srcId) -> IllegalArgumentException\n\n// after\nif (!srcId.equals(dstId)) {\n  gcs.copy(srcId, dstId);\n}","handlingStrategy":"validation","validationCode":"if (srcId.getBucketName().equals(dstId.getBucketName())\n    && srcId.getObjectName().equals(dstId.getObjectName())) {\n  return; // self-copy is a no-op for this caller\n}\ngcs.copy(srcId, dstId);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive destinations from sources only through transformations guaranteed to change the name.","Add unit tests asserting src != dst on every copy call path.","Log src/dst ids at call sites so mis-built arguments are visible in logs."],"tags":["gcs","copy","illegal-argument","self-copy","validation"],"backgroundTag":"copy-source-equals-destination","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}