hashicorp/terraform · warning

error reading source block

Error message

error reading source block %d: %w

What it means

Thrown by uploadPartsWorker() when reading from a section reader fails during multipart upload part preparation. The section reader is backed by bytes.NewReader over the in-memory state data, so a read failure is theoretically near-impossible — bytes.NewReader.Read never returns an error for non-nil buffers. This is defensive error handling for an edge case that should not occur under normal conditions.

Solutions

  1. Check for concurrent modification of the upload data buffer — ensure m.Data is not being mutated during the multipart upload.
  2. Verify memory integrity — this error is not expected under normal Go execution and may indicate a deeper system issue.
  3. Add logging to capture the exact error value and blockNumber to diagnose the unlikely root cause.
  4. If this recurs, the fallback in client.go:147 (single-part upload) should still succeed for sizes <= MaxFilePartSize.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the data buffer before multipart upload
func validateUploadData(data []byte) error {
    if data == nil {
        return fmt.Errorf("upload data is nil")
    }
    if len(data) == 0 {
        return fmt.Errorf("upload data is empty")
    }
    return nil
}

Try / catch

// Wrap multipart upload with graceful error handling
if err := multipartUploadData.multiPartUploadImpl(ctx); err != nil {
    if strings.Contains(err.Error(), "error reading source block") {
        // Defensive error — log and fall back to single-part upload
        logger.Warn("section read failed, falling back to single-part upload", "error", err)
        return c.uploadSinglePartObject(ctx, data, sum[:])
    }
    return err
}

Prevention

When it happens

Trigger: block.section.Read(buffer) returns a non-nil error. Since block.section is an io.NewSectionReader wrapping bytes.NewReader(m.Data), the only way Read returns an error is if the underlying reader is in an invalid state or buffer allocation produced a nil reader path — both effectively unreachable with well-formed data.

Common situations: This error is practically dead code in production. If encountered, it suggests memory corruption, a nil m.Data that somehow passed earlier nil-checks, or a concurrency issue where the underlying data was modified during upload.

Related errors


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

Appendix: source

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

func SplitSizeToOffsetsAndLimits(size int64) ([]int64, int64, error) {
	partSize := DefaultFilePartSize
	totalParts := (size + partSize - 1) / partSize
	if totalParts > MaxCount {
		return nil, 0, fmt.Errorf("file exceeds maximum part count")
	}
	offsets := make([]int64, totalParts)
	for i := range offsets {
		offsets[i] = int64(i) * partSize
	}
	return offsets, partSize, nil
}

func (ctx *objectStorageMultiPartUploadContext) uploadPartsWorker() {
	for block := range ctx.sourceBlocks {
		buffer := make([]byte, block.section.Size())
		_, err := block.section.Read(buffer)
		if err != nil {
			ctx.errChan <- fmt.Errorf("error reading source block %d: %w", block.blockNumber, err)
			return
		}
		tmpLength := int64(len(buffer))
		sum := md5.Sum(buffer)
		uploadPartRequest := &objectstorage.UploadPartRequest{
			UploadId:       ctx.multipartUploadResponse.UploadId,
			ObjectName:     ctx.multipartUploadResponse.Object,
			NamespaceName:  ctx.multipartUploadResponse.Namespace,
			BucketName:     ctx.multipartUploadResponse.Bucket,
			ContentLength:  &tmpLength,
			UploadPartBody: io.NopCloser(bytes.NewReader(buffer)),
			UploadPartNum:  block.blockNumber,
			ContentMD5:     common.String(base64.StdEncoding.EncodeToString(sum[:])),
			RequestMetadata: common.RequestMetadata{
				RetryPolicy: getDefaultRetryPolicy(),
			},
		}

View on GitHub (pinned to d32a084675)