{"record":{"id":"fc4d6b0ccb9da51c","repo":"apache/flink","slug":"interrupted-while-uploading-object-for-key","errorCode":null,"errorMessage":"Interrupted while uploading object for key: {}","messagePattern":"Interrupted while uploading object for key: (.+?)","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":273,"sourceCode":"                                                if (encryptionConfig.hasEncryptionContext()) {\n                                                    req.ssekmsEncryptionContext(\n                                                            encryptionConfig\n                                                                    .serializeEncryptionContext());\n                                                }\n                                            }\n                                        }\n                                    })\n                            .source(inputFile.toPath())\n                            .build();\n\n            FileUpload fileUpload = transferManager.uploadFile(uploadRequest);\n            CompletedFileUpload completedUpload;\n            try {\n                completedUpload = fileUpload.completionFuture().get();\n            } catch (InterruptedException e) {\n                fileUpload.completionFuture().cancel(true);\n                Thread.currentThread().interrupt();\n                throw new IOException(\"Interrupted while uploading object for key: \" + key, e);\n            } catch (ExecutionException e) {\n                throw new IOException(\n                        \"Failed to async upload object for key: \" + key, e.getCause());\n            }\n            return new PutObjectResult(completedUpload.response().eTag());\n        } catch (IOException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new IOException(\"Failed to async upload object for key: \" + key, e);\n        }\n    }\n\n    /**\n     * Completes a multipart upload by assembling previously uploaded parts.\n     *\n     * <p><b>Recovery Scenario:</b> If a {@link NoSuchUploadException} is thrown, this may indicate\n     * that the upload was already completed (possibly by a previous attempt during recovery). In\n     * this case, we check if the object exists and return its metadata. This handles the scenario","sourceCodeStart":255,"sourceCodeEnd":291,"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#L255-L291","documentation":"In the S3TransferManager upload path, the code blocks on fileUpload.completionFuture().get(). If that thread is interrupted (task cancellation, shutdown hook, checkpoint timeout cancelling the operator), the future is cancelled with mayInterruptIfRunning, the interrupt flag is restored on the current thread, and this IOException is thrown. Restoring the interrupt status matters: Flink relies on it to propagate cancellation cleanly.","triggerScenarios":"Flink cancels the job/task while a TransferManager upload of a part or small object is in flight; a checkpoint timeout triggers operator thread interruption; user code interrupts the writer thread; test harness teardown interrupts upload threads.","commonSituations":"Job cancellation during large S3 writes; failover where the writer thread is interrupted mid-upload; abrupt MiniCluster shutdown in tests leaving uploads unfinished (normally benign — the upload is cancelled by design).","solutions":["Treat as cancellation, not data loss: do not swallow it; let the IOException propagate so Flink's cancellation machinery finishes cleanup (abort of the multipart upload happens via the writer's cleanup path).","If your operator catches InterruptedException-shaped errors, ensure you re-interrupt/rethrow rather than retrying the upload — the transfer was cancelled(true) and cannot be resumed.","To reduce interruption windows, keep individual uploads (part sizes) bounded so cancellation lands between uploads.","On restart, rely on RecoverableWriter state to re-upload from the last persisted part instead of restarting the file from zero."],"exampleFix":"// before — swallowing an interrupted upload and retrying\ntry { ops.putObject(key, file); }\ncatch (IOException e) { ops.putObject(key, file); } // may retry a cancelled transfer\n\n// after — honor cancellation\ntry { ops.putObject(key, file); }\ncatch (IOException e) {\n    if (Thread.currentThread().isInterrupted()) throw e; // propagate cancellation\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (IOException e) { if (Thread.currentThread().isInterrupted() || (e.getCause() instanceof InterruptedException)) { cleanupAndPropagateForCancellation(e); /* no retry */ } else throw e; }","preventionTips":["Never swallow interruption; the code already restored the interrupt flag — preserve it.","Keep part sizes bounded so cancellation windows are short and cleanup is quick.","Design recovery around the RecoverableWriter's persisted offset so interrupted uploads resume, not restart."],"tags":["s3","interruption","cancellation","transfer-manager","concurrency","flink"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}