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
- At the call site, guard the upload: only call Put after confirming len(data) > 0 and the state serializes non-empty JSON.
- Inspect the state manager output that produced the empty bytes (enable TF_LOG=TRACE) to find where serialization produced nothing.
- 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
- Check len(data) > 0 at every call site of Put/uploadSinglePartObject.
- Serialize state with a routine that cannot return empty bytes on success.
- In custom integrations, validate before invoking the backend.
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
- object %q is empty
- failed to upload object: %w
- failed to unmarshal JSON data into LockInfo struct: %w
- failed to access object '%s' in bucket '%s': %w
- failed to access object HttpStatusCode: %d OpcRequestId: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/a42b08cc52b56bd4.
Report an issue: GitHub.