hashicorp/terraform · error
failed to upload part
Error message
failed to upload part: %s
What it means
After the workers complete, the code iterates the osUploadPartResponses channel and rejects any response whose error is non-nil OR whose partNumber/ETag is nil. Indicates at least one part-upload returned an error or a malformed success (missing ETag/partNumber). Note the format uses %s on response.error, which can be nil when only partNumber/ETag is missing — the message may read 'failed to upload part: %!s(<nil>)'.
Solutions
- Check worker logs (TF_LOG=DEBUG) for the per-part error — the aggregated message can hide it when response.error is nil.
- Reduce parallelism pressure — fewer/smaller parts (raise DefaultFilePartSize) lowers the chance of any single part failing.
- Refresh long-lived credentials before starting a large upload; multipart uploads can outlive a short token TTL.
- Retry the whole Put; the upload was aborted/abandoned on this path.
Example fix
// before: 10 workers upload parts against a saturated link -> intermittent 317 // after: raise part size to reduce part count and per-part bandwidth pressure var DefaultFilePartSize int64 = 256 * 1024 * 1024 // and run with TF_LOG=DEBUG to capture the per-part error // terraform apply -auto-approve 2>tf.log // grep 'failed to upload part' tf.log
Defensive patterns
Strategy: retry
Type guard
func isPartUploadErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to upload part")
} Try / catch
// On per-part failure, capture the worker error (it may be nil when only ETag is missing)
if response.error != nil || response.partNumber == nil || response.response.ETag == nil {
err := response.error
if err == nil { err = fmt.Errorf("part %d upload returned nil ETag/partNumber", deref(response.partNumber)) }
return nil, fmt.Errorf("failed to upload part: %w", err)
} Prevention
- Run with TF_LOG=DEBUG to capture the per-part worker error (the aggregate can show nil).
- Raise DefaultFilePartSize to reduce part count and per-part bandwidth pressure.
- Refresh long-lived credentials before starting a large multipart upload.
- Throttle CI concurrency against the same bucket to avoid per-part throttling.
When it happens
Trigger: A worker's UploadPart returned an OCI/transport error (throttling, auth expiry, 5xx); a worker returned success but with nil ETag or nil partNumber; transient failures under 10-way parallel uploads.
Common situations: Bandwidth saturated by 10 concurrent part uploads; token expiring during a long multipart upload; throttling (429) under high CI concurrency; partial network failure affecting one worker.
Related errors
- failed to access object HttpStatusCode
- failed to commit multipart upload
- failed to upload part
- not all parts uploaded successfully, multipart upload…
- error creating multipart upload
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/24599b29238a6e50.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/oci/multipart_upload.go:126
ctx.uploadPartsWorker()
}()
}
wg.Wait()
close(osUploadPartResponses)
close(errChan)
// Collect errors from workers
for workerErr := range errChan {
if workerErr != nil {
return workerErr
}
}
commitMultipartUploadPartDetails := make([]objectstorage.CommitMultipartUploadPartDetails, len(sourceBlocks))
i := 0
for response := range osUploadPartResponses {
if response.error != nil || response.partNumber == nil || response.response.ETag == nil {
return fmt.Errorf("failed to upload part: %s", response.error)
}
partNumber, etag := *response.partNumber, *response.response.ETag
commitMultipartUploadPartDetails[i] = objectstorage.CommitMultipartUploadPartDetails{
PartNum: common.Int(partNumber),
Etag: common.String(etag),
}
i++
}
if len(commitMultipartUploadPartDetails) != len(sourceBlocks) {
abortReq := objectstorage.AbortMultipartUploadRequest{
UploadId: multipartUploadResponse.MultipartUpload.UploadId,
NamespaceName: multipartUploadResponse.Namespace,
BucketName: multipartUploadResponse.Bucket,
ObjectName: multipartUploadResponse.Object,
}
_, abortErr := multipartUploadData.client.objectStorageClient.AbortMultipartUpload(context.Background(), abortReq)
if abortErr != nil {View on GitHub (pinned to d32a084675)