hashicorp/terraform · error

uploadSinglePartObject: data is empty

Error message

uploadSinglePartObject: data is empty

What it means

Raised in uploadSinglePartObject as a defensive guard when the data slice passed to the uploader is empty (len(data) == 0). Put calls uploadSinglePartObject with the serialized state, so reaching here means an empty state payload is being written. It prevents writing a 0-byte state object (which would later trip the 'object is empty' read error at client.go:123).

Source

Thrown at internal/backend/remote-state/oci/client.go:165

		if err != nil && dataSize <= MaxFilePartSize {
			logger.Error(fmt.Sprintf("Multipart upload failed, falling back to single part upload: %v", err))
			err = c.uploadSinglePartObject(ctx, data, sum[:])
		}
	} else {
		err = c.uploadSinglePartObject(ctx, data, sum[:])
	}
	if err != nil {
		return diags.Append(err)
	}

	return diags
}

func (c *RemoteClient) uploadSinglePartObject(ctx context.Context, data, sum []byte) error {
	logger := ctx.Value("logger").(hclog.Logger).Named("singlePartUpload")
	logger.Info("Uploading single part object")
	if len(data) == 0 {
		return fmt.Errorf("uploadSinglePartObject: data is empty")
	}

	contentType := "application/json"

	putRequest := objectstorage.PutObjectRequest{
		ContentType:   common.String(contentType),
		NamespaceName: common.String(c.namespace),
		ObjectName:    common.String(c.path),
		BucketName:    common.String(c.bucketName),
		PutObjectBody: io.NopCloser(bytes.NewReader(data)),
		ContentMD5:    common.String(base64.StdEncoding.EncodeToString(sum)),
		RequestMetadata: common.RequestMetadata{
			RetryPolicy: getDefaultRetryPolicy(),
		},
	}

	// Handle encryption settings
	if c.kmsKeyID != "" {

View on GitHub (pinned to c9def3e214)

Solutions

  1. At the call site, guard the upload: only call Put after confirming len(data) > 0 and the state serializes non-empty JSON.
  2. Inspect the state manager output that produced the empty bytes (enable TF_LOG=TRACE) to find where serialization produced nothing.
  3. If this is a custom integration, validate the payload before invoking Put rather than relying on this internal guard.

Example fix

// before
diags := client.Put(data) // data may be empty
// after
if len(data) == 0 {
    return tfdiags.Diagnostics{}.Append(fmt.Errorf("refusing to Put empty state payload"))
}
diags := client.Put(data)
Defensive patterns

Strategy: validation

Validate before calling

// Validate payload size before calling Put:
if len(data) == 0 {
    return fmt.Errorf("refusing to upload empty state payload")
}

Try / catch

func (c *RemoteClient) uploadSinglePartObject(ctx context.Context, data, sum []byte) error {
    if len(data) == 0 {
        return fmt.Errorf("uploadSinglePartObject: data is empty")
    }
    // ...
}

Prevention

When it happens

Trigger: RemoteClient.Put or the multipart fallback calls uploadSinglePartObject(ctx, data, sum) with len(data)==0. This happens if the state manager serializes to zero bytes, or if an internal caller invokes Put with an empty slice.

Common situations: An upstream bug or a custom integration calling client.Put(nil)/Put([]byte{}); a state serialization routine returning empty bytes due to a panic/short-circuit; testing harness passing empty data.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/a42b08cc52b56bd4. Report an issue: GitHub.