hashicorp/terraform · error

object is empty

Error message

object %q is empty

What it means

The state object exists (Head succeeded, Get succeeded, MD5 was computed) but its body is zero bytes. The backend refuses to return a nil/empty payload and instead surfaces this corruption error so that init does not silently treat the workspace as new.

Solutions

  1. Restore the state from a backup or, if object versioning is enabled, promote a previous non-empty version.
  2. If the state is genuinely meant to be empty/absent, delete the object so init recreates it as a proper empty state.
  3. Enable Object Versioning on the bucket so future corruption is recoverable.
  4. Audit who/what has OBJECT_OVERWRITE on the bucket and remove stray writers.

Example fix

# before: someone truncated the state object
oci os object head --bucket-name tf-state --name prod.tfstate  # Content-Length: 0
# after: restore from a prior version, then re-run init
oci os object-version copy-to ...   # or restore latest non-empty version
# or, if state is intentionally empty, delete the object:
oci os object delete --bucket-name tf-state --name prod.tfstate
Defensive patterns

Strategy: validation

Validate before calling

// Treat a zero-length object as corruption and refuse to proceed silently
func assertNonEmpty(p *remote.Payload, path string) error {
    if p != nil && len(p.Data) == 0 {
        return fmt.Errorf("object %q is empty — restore from backup/versioning or delete and re-init", path)
    }
    return nil
}

Try / catch

// On empty-object error, prompt recovery rather than silent retry
if err != nil && strings.Contains(err.Error(), "is empty") {
    // restore from object versioning or delete the object so init recreates it
}

Prevention

When it happens

Trigger: Someone manually created or truncated the object at the configured key; a previous interrupted write left a zero-byte object; bucket replication/lifecycle created an empty placeholder; an external process wrote an empty file to the same key.

Common situations: Manual bucket tampering; a failed migration from another backend that wrote the empty target object; cross-region replication in progress producing a placeholder; object versioning confusion where the 'current' version is empty.

Related errors


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

Appendix: 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 d32a084675)