hashicorp/terraform · error
uploadSinglePartObject: data is empty
Error message
uploadSinglePartObject: data is empty
What it means
Defensive guard in uploadSinglePartObject: it rejects any call where the data slice is empty. State writes should always carry serialized state; an empty slice indicates a caller bug upstream rather than a legitimate write.
Solutions
- Ensure the state serializer always produces non-empty bytes before calling Put.
- For initial empty state, rely on StateMgr's init flow (it writes states.NewState(), which is non-empty JSON) rather than calling Put([]byte{}).
- If hit in custom code, debug why len(data)==0 upstream — this guard means the precondition is wrong.
Example fix
// before: caller uploads empty bytes
client.Put([]byte{}) // -> "uploadSinglePartObject: data is empty"
// after: write a proper (possibly empty) state object
empty := states.NewState()
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(empty)
client.Put(buf.Bytes()) Defensive patterns
Strategy: validation
Validate before calling
// Reject empty payloads upstream, before reaching uploadSinglePartObject
func putState(c *RemoteClient, data []byte) tfdiags.Diagnostics {
if len(data) == 0 { return tfdiags.Diagnostics{}.Append(fmt.Errorf("refusing to upload empty state")) }
return c.Put(data)
} Prevention
- Always serialize through states.NewState() (non-empty JSON) for initial state.
- Unit-test the state serializer to ensure it never returns zero bytes.
- Treat an empty payload as a caller bug, not a normal condition.
- Do not bypass StateMgr's init flow when seeding a new workspace.
When it happens
Trigger: Caller passed a nil/zero-length byte slice to uploadSinglePartObject; the state serializer returned no bytes; a code path that should have used the empty-state init flow in StateMgr instead tried to Put nothing.
Common situations: Bug in a fork that produces empty serialized state; test fixtures calling Put([]byte{}); a refactor that bypassed the `state == nil -> states.NewState()` init in backend_state.go:94.
Related errors
- auth must be one of ' ' or ' ' or ' ' or ' ' or ' ' or
- can't delete default state
- failed to access object HttpStatusCode
- failed to access object
- failed to get existing lock file
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/a42b08cc52b56bd4.
Report an issue: GitHub.
Appendix: 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 d32a084675)