hashicorp/terraform · error

object %q is empty

Error message

object %q is empty

What it means

Raised in getObject when GetObject and the body read both succeeded but the resulting payload has zero bytes (len(payload.Data) == 0). The comment at client.go:121 explicitly notes this replaces a prior 'nil, nil' that silently returned an empty state, so an empty-but-existing object is now treated as a corruption error rather than a fresh init.

Source

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

	contentArray, err := io.ReadAll(getResponse.Content)
	if err != nil {
		return nil, fmt.Errorf("unable to read 'content' from response: %w", err)
	}

	// Compute MD5 hash
	md5Hash := getResponse.ContentMd5
	if md5Hash == nil || len(*md5Hash) == 0 {
		md5Hash = getResponse.OpcMultipartMd5
	}
	// Construct payload
	payload := &remote.Payload{
		Data: contentArray,
		MD5:  []byte(*md5Hash),
	}

	// Return an error instead of `nil, nil` if the object is empty
	if len(payload.Data) == 0 {
		return nil, fmt.Errorf("object %q is empty", c.path)
	}

	return payload, nil
}

func (c *RemoteClient) Put(data []byte) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	logger := logWithOperation("upload-state-file").Named(c.path)
	ctx := context.WithValue(context.Background(), "logger", logger)
	dataSize := int64(len(data))
	sum := md5.Sum(data)
	var err error
	if dataSize > DefaultFilePartSize {
		logger.Info("Using Multipart Feature")
		var multipartUploadData = MultipartUploadData{
			client: c,
			Data:   data,

View on GitHub (pinned to c9def3e214)

Solutions

  1. List object versions: 'oci os object list-versions --bucket-name <b> --namespace <ns> --prefix <key>'; if versioning is on, restore the last non-empty version.
  2. If you have a known-good backup, re-upload it (oci os object put) to the same key, or delete the 0-byte object and run 'terraform init' to let Terraform treat the state as absent.
  3. Audit who/what wrote the 0-byte object via the OCI audit log around the object's last-modified time.
  4. If the empty object is unexpected on a fresh backend, confirm no other pipeline shares the same key and overwrote it with empty content.
Defensive patterns

Strategy: validation

Validate before calling

// After a successful Get, validate payload before trusting it as state:
payload, err := c.getObject(ctx)
if err == nil && len(payload.Data) == 0 {
    return nil, fmt.Errorf("object %q is empty", c.path) // (this is what the lib now does)
}

Try / catch

if len(payload.Data) == 0 {
    return nil, fmt.Errorf("object %q is empty", c.path)
}

Prevention

When it happens

Trigger: The state object at c.path exists (HeadObject did not 404) and GetObject returned HTTP 200, but the object body is empty. Reachable when something wrote a 0-byte object to the state key: a botched upload, an aborted multipart upload that left a 0-byte object, or an external process truncating the object.

Common situations: A previous 'terraform apply' crashed during upload leaving a 0-byte tfstate; someone manually emptied the object; a migration/import tool created the key without content; an aborted multipart commit leaving a placeholder object.

Related errors


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