apache/flink · error · IOException

Failed to complete multipart upload for key: {}

Error message

Failed to complete multipart upload for key: {}

What it means

completeMultipartUpload() catches NoSuchUploadException and, as a recovery heuristic, checks whether the object already exists (getObjectMetadata) — the upload may have been completed by a previous attempt before a failover. If that existence check ALSO fails (object absent), this IOException is thrown wrapping the ORIGINAL NoSuchUploadException: the upload is gone and there is no committed object, so the write is genuinely lost from this handle's perspective. This is the failure that indicates an aborted/expired upload rather than a completed one.

Source

Thrown at flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/writer/NativeS3ObjectOperations.java:336

                    CompleteMultipartUploadRequest.builder()
                            .bucket(bucketName)
                            .key(key)
                            .uploadId(uploadId)
                            .multipartUpload(
                                    CompletedMultipartUpload.builder()
                                            .parts(completedParts)
                                            .build())
                            .build();

            CompleteMultipartUploadResponse response = s3Client.completeMultipartUpload(request);
            return new CompleteMultipartUploadResult(
                    bucketName, key, response.eTag(), response.location());
        } catch (NoSuchUploadException e) {
            try {
                ObjectMetadata metadata = getObjectMetadata(key);
                return new CompleteMultipartUploadResult(bucketName, key, metadata.getETag(), null);
            } catch (IOException checkEx) {
                throw new IOException("Failed to complete multipart upload for key: " + key, e);
            }
        } catch (S3Exception e) {
            throw new IOException("Failed to complete multipart upload for key: " + key, e);
        }
    }

    public void abortMultiPartUpload(String key, String uploadId) throws IOException {
        try {
            AbortMultipartUploadRequest request =
                    AbortMultipartUploadRequest.builder()
                            .bucket(bucketName)
                            .key(key)
                            .uploadId(uploadId)
                            .build();

            s3Client.abortMultipartUpload(request);
        } catch (S3Exception e) {
            throw new IOException(

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure exactly one committer per upload: rely on Flink's exactly-once committer semantics / FileSink committer rather than calling commit() from multiple recovered attempts.
  2. Review bucket lifecycle AbortIncompleteMultipartUpload days vs your longest expected recovery window; raise it above your max checkpoint/savepoint restore delay.
  3. Examine the cause: NoSuchUpload + missing object means this write must be redone — re-run from the last good checkpoint (data since the upload start is not recoverable from this handle).
  4. Audit external automation that aborts multipart uploads or deletes objects in the target prefix.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before committing a recovered upload, confirm it is still live
try {
    s3Client.listMultipartUploads(b -> b.bucket(bucket).prefix(key));
    // if uploadId absent from listing AND object absent via headObject -> upload lost: plan re-write
} catch (S3Exception e) { /* surface config/permission problem early */ }

Try / catch

catch (IOException e) { if (e.getCause() instanceof NoSuchUploadException) { // upload aborted/expired and object absent -> data must be re-written from last checkpoint; never retry this commit } else throw e; }

Prevention

When it happens

Trigger: Multipart upload aborted by another attempt (duplicate committer after recovery), aborted by an S3 lifecycle rule (AbortIncompleteMultipartUpload), uploadId expired, or the complete succeeded on a previous attempt but the object was since deleted — then getObjectMetadata throws and this error surfaces with the NoSuchUpload cause.

Common situations: Two committers racing after task failover (one aborts, one completes); bucket lifecycle rules aggressively cleaning incomplete uploads (e.g. 1 day) while jobs hold recoverable state longer; user/scripts deleting objects mid-recovery; cross-run dedup logic aborting 'stale' uploads it should not have.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/70b39dfb0a0faf4d. Report an issue: GitHub.