apache/hadoop · error · PathIOException
No upload parts in multipart upload
Error message
No upload parts in multipart upload
What it means
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.
Source
Thrown at hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/WriteOperationHelper.java:311
* @param destKey destination of the commit
* @param uploadId multipart operation Id
* @param partETags list of partial uploads
* @param length length of the upload
* @param putOptions put object options
* @param retrying retrying callback
* @return the result of the operation.
* @throws IOException on problems.
*/
@Retries.RetryTranslated
private CompleteMultipartUploadResponse finalizeMultipartUpload(
String destKey,
String uploadId,
List<CompletedPart> partETags,
long length,
PutObjectOptions putOptions,
Retried retrying) throws IOException {
if (partETags.isEmpty()) {
throw new PathIOException(destKey,
"No upload parts in multipart upload");
}
try (AuditSpan span = activateAuditSpan()) {
CompleteMultipartUploadResponse uploadResult;
uploadResult = invoker.retry("Completing multipart upload id " + uploadId,
destKey,
true,
retrying,
() -> {
final CompleteMultipartUploadRequest.Builder requestBuilder =
getRequestFactory().newCompleteMultipartUploadRequestBuilder(destKey, uploadId, partETags, putOptions);
return writeOperationHelperCallbacks.completeMultipartUpload(requestBuilder.build());
});
return uploadResult;
}
}
/**View on GitHub (pinned to 2add963021)
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
Example fix
// before
helper.completeMultipartUpload(uploadId, partETags, length); // partETags is empty
// after - guard before completing; empty objects take the putObject path
if (partETags.isEmpty()) {
helper.abortMultipartUpload(key, uploadId);
if (length == 0) {
helper.putObject(key, new byte[0]); // empty object via single PUT
return;
}
throw new IOException("No parts uploaded for " + key + " (" + length + " bytes)");
}
helper.completeMultipartUpload(uploadId, partETags, length); Defensive patterns
Strategy: validation
Validate before calling
if (partETags == null || partETags.isEmpty()) {
throw new IOException("Refusing to complete multipart upload "
+ uploadId + " with no uploaded parts");
}
helper.completeMultipartUpload(uploadId, partETags, length); Try / catch
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
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- Unable to create OutputStream with the given multipart uploa
- Multipart IO request {sdkRequest} rejected {header}
- Multi-part uploader not supported for Client side encryption
- Cannot seek to a negative offset <targetPos>
- <uri>: Stream is closed!
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/ef3e8cfcc7230c14.
Report an issue: GitHub.