hashicorp/terraform · error

failed to commit multipart upload

Error message

failed to commit multipart upload: %s

What it means

The final CommitMultipartUpload call failed after all parts uploaded successfully. The object is not finalized. Notably, on this failure path the code does NOT call AbortMultipartUpload — the multipart upload is left open, which can leak storage until lifecycle rules clean it up.

Solutions

  1. Inspect the wrapped error's OCI code via errors.As(common.ServiceError) — OpcRequestId and code pinpoint the commit failure.
  2. Retry the whole Put once the root cause is addressed (the uncommitted multipart upload on the failed attempt will linger).
  3. Configure a bucket lifecycle rule to expire Incomplete/Uncommitted Multipart Uploads so failed commits do not leak storage.
  4. Consider patching the backend to call AbortMultipartUpload on this path (currently missing) to avoid manual cleanup.

Example fix

// before: commit fails, leaves an open multipart upload that leaks storage
//   return fmt.Errorf("failed to commit multipart upload: %s", err)
// after: abort on commit failure to avoid leaking the upload
_, err = client.CommitMultipartUpload(ctx, commitReq)
if err != nil {
    abortReq := objectstorage.AbortMultipartUploadRequest{
        UploadId: multipartUploadResponse.MultipartUpload.UploadId,
        NamespaceName: multipartUploadResponse.Namespace,
        BucketName: multipartUploadResponse.Bucket,
        ObjectName: multipartUploadResponse.Object,
    }
    _ = client.AbortMultipartUpload(ctx, abortReq)
    return fmt.Errorf("failed to commit multipart upload: %s", err)
}
Defensive patterns

Strategy: retry

Type guard

func isCommitError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to commit multipart upload")
}

Try / catch

// On commit failure, abort the now-leaked multipart upload, then retry the Put
if err := commit(); err != nil {
    _ = abortMultipartUpload(multipartUploadResponse) // currently missing in source
    return fmt.Errorf("failed to commit multipart upload: %w", err)
}

Prevention

When it happens

Trigger: Commit API returned 4xx (wrong/missing part etag, part list misaligned) or 5xx (transient service failure, throttling); a part's ETag was stale by commit time; auth expired between upload and commit.

Common situations: Transient 5xx at commit; throttling under high concurrency; long uploads whose token expired before commit; mismatched part list after retries.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/00e42de86a5ef8a3. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/oci/multipart_upload.go:163

			logger.Error(fmt.Sprintf("Failed to abort multipart upload: %s", abortErr))
		}
		return fmt.Errorf("not all parts uploaded successfully, multipart upload aborted")
	}

	commitMultipartUploadRequest := objectstorage.CommitMultipartUploadRequest{
		UploadId:           multipartUploadResponse.MultipartUpload.UploadId,
		NamespaceName:      multipartUploadResponse.Namespace,
		BucketName:         multipartUploadResponse.Bucket,
		ObjectName:         multipartUploadResponse.Object,
		OpcClientRequestId: multipartUploadResponse.OpcClientRequestId,
		RequestMetadata:    multipartUploadRequest.RequestMetadata,
		CommitMultipartUploadDetails: objectstorage.CommitMultipartUploadDetails{
			PartsToCommit: commitMultipartUploadPartDetails,
		},
	}
	_, err = multipartUploadData.client.objectStorageClient.CommitMultipartUpload(context.Background(), commitMultipartUploadRequest)
	if err != nil {
		return fmt.Errorf("failed to commit multipart upload: %s", err)
	}

	return nil
}
func (m MultipartUploadData) objectMultiPartSplit() ([]objectStorageSourceBlock, error) {
	dataSize := int64(len(m.Data))
	offsets, partSize, err := SplitSizeToOffsetsAndLimits(dataSize)
	if err != nil {
		return nil, fmt.Errorf("error splitting data into parts: %s", err)
	}
	sourceBlocks := make([]objectStorageSourceBlock, len(offsets))
	for i := range offsets {
		start := offsets[i]
		end := start + partSize
		if end > dataSize {
			end = dataSize
		}
		sourceBlocks[i] = objectStorageSourceBlock{

View on GitHub (pinned to d32a084675)