Skip to content

TransferManager doesn't handle constraint failures gracefully #1644

@bencampion

Description

@bencampion

I'm using TransferManager to conditionally copy an object if it has a matching ETag. I would like to be able to identify when a transfer has failed because the constraints weren't met (as opposed to failing for some other reason), but that doesn't appear to be possible due to the way TransferManager handles failing constraints.

I'm copying the object like this:

CopyObjectRequest copyRequest = new CopyObjectRequest(srcBucket, srcKey, destBucket, destKey);
copyRequest.setMatchingETagConstraints(Collections.singletonList(expectedETag));
s3TransferManager.copy(copyRequest).waitForCopyResult();

If the ETag doesn't match then this exception is throw:

com.amazonaws.AmazonClientException: Unable to complete transfer: null
	at com.amazonaws.services.s3.transfer.internal.AbstractTransfer.unwrapExecutionException(AbstractTransfer.java:286) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at com.amazonaws.services.s3.transfer.internal.AbstractTransfer.rethrowExecutionException(AbstractTransfer.java:265) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at com.amazonaws.services.s3.transfer.internal.CopyImpl.waitForCopyResult(CopyImpl.java:67) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
<SNIP>
Caused by: java.lang.NullPointerException: null
	at com.amazonaws.services.s3.transfer.internal.CopyCallable.copyInOneChunk(CopyCallable.java:154) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at com.amazonaws.services.s3.transfer.internal.CopyCallable.call(CopyCallable.java:134) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at com.amazonaws.services.s3.transfer.internal.CopyMonitor.call(CopyMonitor.java:132) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at com.amazonaws.services.s3.transfer.internal.CopyMonitor.call(CopyMonitor.java:43) ~[aws-java-sdk-s3-1.11.351.jar!/:na]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_111]
	... 3 common frames omitted

The NullPointerException occurs because the return value from s3.copyObject(copyObjectRequest) (which returns null when constraints aren't met) is not checked before calling copyObjectResult.getETag(): https://github.com/aws/aws-sdk-java/blob/1.11.351/aws-java-sdk-s3/src/main/java/com/amazonaws/services/s3/transfer/internal/CopyCallable.java#L146-L154.

It looks like there may be other parts of the code that aren't checking nullable return values: https://github.com/aws/aws-sdk-java/blob/1.11.351/aws-java-sdk-s3/src/main/java/com/amazonaws/services/s3/transfer/internal/CopyPartCallable.java#L41

I'm not sure what the correct behaviour here should be. I was expecting a AmazonS3Exception with the appropriate status code, but I'm not sure how you'd achieve that as AmazonS3Client is documented to swallow these exceptions and return null. Throwing a AmazonClientException with a message about constraints would be an improvement for debugging, but it's not a good way to programmatically detect the reason the transfer had failed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestA feature should be added or improved.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions