{"record":{"id":"ef3e8cfcc7230c14","repo":"apache/hadoop","slug":"no-upload-parts-in-multipart-upload","errorCode":null,"errorMessage":"No upload parts in multipart upload","messagePattern":"No upload parts in multipart upload","errorType":"exception","errorClass":"PathIOException","httpStatus":null,"severity":"error","filePath":"hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/WriteOperationHelper.java","lineNumber":311,"sourceCode":"   * @param destKey destination of the commit\n   * @param uploadId multipart operation Id\n   * @param partETags list of partial uploads\n   * @param length length of the upload\n   * @param putOptions put object options\n   * @param retrying retrying callback\n   * @return the result of the operation.\n   * @throws IOException on problems.\n   */\n  @Retries.RetryTranslated\n  private CompleteMultipartUploadResponse finalizeMultipartUpload(\n      String destKey,\n      String uploadId,\n      List<CompletedPart> partETags,\n      long length,\n      PutObjectOptions putOptions,\n      Retried retrying) throws IOException {\n    if (partETags.isEmpty()) {\n      throw new PathIOException(destKey,\n          \"No upload parts in multipart upload\");\n    }\n    try (AuditSpan span = activateAuditSpan()) {\n      CompleteMultipartUploadResponse uploadResult;\n      uploadResult = invoker.retry(\"Completing multipart upload id \" + uploadId,\n          destKey,\n          true,\n          retrying,\n          () -> {\n            final CompleteMultipartUploadRequest.Builder requestBuilder =\n                getRequestFactory().newCompleteMultipartUploadRequestBuilder(destKey, uploadId, partETags, putOptions);\n            return writeOperationHelperCallbacks.completeMultipartUpload(requestBuilder.build());\n          });\n      return uploadResult;\n    }\n  }\n\n  /**","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/WriteOperationHelper.java#L293-L329","documentation":"PathIOException from WriteOperationHelper.finalizeMultipartUpload when completeMultipartUpload would be invoked with an empty partETags list. Multipart completion requires at least one uploaded part (with its ETag), so S3A refuses rather than send an invalid CompleteMultipartUploadRequest to S3.","triggerScenarios":"Using WriteOperationHelper directly and calling its multipart-completion API before any part was uploaded; buffering implementations or custom committers that never flush a part before completing; empty-input flows that reached the multipart completion path instead of a single putObject.","commonSituations":"Custom code built on WriteOperationHelper that skips uploadPart for zero-byte inputs; bugs in third-party committers or output formats; jobs writing empty files through a nonstandard path instead of the S3AFileSystem output stream (which handles empty files with a plain putObject).","solutions":["Upload at least one part before completing; abort the upload instead when nothing was written","For empty files, use the standard S3AFileSystem.create()/output stream or a direct putObject rather than multipart","If this surfaces inside a committer, upgrade hadoop-aws - bundled committers handle empty-file uploads correctly","Add an assertion partETags.size() > 0 in your buffering code to catch the bug earlier"],"exampleFix":"// before\nhelper.completeMultipartUpload(uploadId, partETags, length); // partETags is empty\n\n// after - guard before completing; empty objects take the putObject path\nif (partETags.isEmpty()) {\n  helper.abortMultipartUpload(key, uploadId);\n  if (length == 0) {\n    helper.putObject(key, new byte[0]); // empty object via single PUT\n    return;\n  }\n  throw new IOException(\"No parts uploaded for \" + key + \" (\" + length + \" bytes)\");\n}\nhelper.completeMultipartUpload(uploadId, partETags, length);","handlingStrategy":"validation","validationCode":"if (partETags == null || partETags.isEmpty()) {\n  throw new IOException(\"Refusing to complete multipart upload \"\n      + uploadId + \" with no uploaded parts\");\n}\nhelper.completeMultipartUpload(uploadId, partETags, length);","typeGuard":null,"tryCatchPattern":"catch PathIOException with 'No upload parts in multipart upload'; it indicates a caller bug in the write path - fix the code, never retry the same empty completion","preventionTips":["Write through S3AFileSystem output streams unless you reimplement their invariants","Unit-test custom writers with 0-byte, tiny, and multi-part inputs","Abort the multipart upload on every failure path to avoid orphaned uploads"],"tags":["s3a","hadoop-aws","multipart-upload","write","pathioexception","writeoperationhelper"],"backgroundTag":"multipart-upload-failed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}