{"record":{"id":"d7666c504f38fafa","repo":"apache/flink","slug":"failed-to-abort-multipart-upload-for-key-s-uplo","errorCode":null,"errorMessage":"Failed to abort multipart upload for key: %s, uploadId: %s","messagePattern":"Failed to abort multipart upload for key: (.+?), uploadId: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/writer/NativeS3ObjectOperations.java","lineNumber":354,"sourceCode":"                throw new IOException(\"Failed to complete multipart upload for key: \" + key, e);\n            }\n        } catch (S3Exception e) {\n            throw new IOException(\"Failed to complete multipart upload for key: \" + key, e);\n        }\n    }\n\n    public void abortMultiPartUpload(String key, String uploadId) throws IOException {\n        try {\n            AbortMultipartUploadRequest request =\n                    AbortMultipartUploadRequest.builder()\n                            .bucket(bucketName)\n                            .key(key)\n                            .uploadId(uploadId)\n                            .build();\n\n            s3Client.abortMultipartUpload(request);\n        } catch (S3Exception e) {\n            throw new IOException(\n                    String.format(\n                            \"Failed to abort multipart upload for key: %s, uploadId: %s\",\n                            key, uploadId),\n                    e);\n        }\n    }\n\n    /**\n     * Deletes an object from S3.\n     *\n     * <p><b>Permission Considerations:</b> Note that S3 may return different error codes depending\n     * on permissions:\n     *\n     * <ul>\n     *   <li>With ListBucket permission: Returns 404 if object doesn't exist\n     *   <li>Without ListBucket permission: Returns 403 (Access Denied) even for non-existent\n     *       objects (for security reasons, to prevent object enumeration)\n     * </ul>","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/writer/NativeS3ObjectOperations.java#L336-L372","documentation":"abortMultiPartUpload() calls S3 AbortMultipartUpload for a (bucket, key, uploadId). Any S3Exception — typically NoSuchUpload (already completed/aborted) or AccessDenied — is wrapped in this formatted IOException carrying both the key and uploadId. Aborting frees storage for uploaded parts; S3 charges for parts of incomplete uploads until abort or lifecycle expiry, so unhandled failures here have a cost dimension.","triggerScenarios":"Aborting an uploadId that was already completed by a sibling attempt or already aborted (NoSuchUpload); aborting after the upload hit a lifecycle rule; IAM missing s3:AbortMultipartUpload on bucket/object; network/5xx during the abort call.","commonSituations":"Cleanup paths during failover racing a concurrent commit; error-handling code that aborts on any exception, including after a successful complete; restrictive IAM policies that grant PutObject but not AbortMultipartUpload.","solutions":["Treat NoSuchUpload on abort as success (upload already gone) — log and continue instead of failing cleanup.","Grant s3:AbortMultipartUpload in the job's IAM policy alongside the put/multipart-list actions.","Retry once on transient 5xx before surfacing; abort is idempotent.","As a backstop for orphaned uploads, set a bucket lifecycle rule AbortIncompleteMultipartUpload (e.g. 7 days)."],"exampleFix":"// before — abort failure breaks cleanup\nops.abortMultiPartUpload(key, uploadId);\n\n// after — tolerate already-gone uploads\ntry { ops.abortMultiPartUpload(key, uploadId); }\ncatch (IOException e) {\n    if (!(e.getCause() instanceof NoSuchUploadException)) throw e;\n    LOG.debug(\"Upload already completed/aborted: {} {}\", key, uploadId);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (IOException e) { if (e.getCause() instanceof NoSuchUploadException) { LOG.debug(\"Upload already gone: {}\", uploadId); /* treat as success */ } else if (((S3Exception) e.getCause()).statusCode() >= 500) { retryAbortOnce(); } else throw e; }","preventionTips":["Treat NoSuchUpload on abort as success — the goal (upload gone) is already met.","Include s3:AbortMultipartUpload in the job role from day one.","Configure a bucket lifecycle rule for AbortIncompleteMultipartUpload as a cost backstop."],"tags":["s3","multipart-upload","cleanup","idempotency","iam","flink"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}