hashicorp/terraform · error

error getting object: %#v

Error message

error getting object: %#v

What it means

Thrown by RemoteClient.getObj after IsObjectExist confirmed the object exists, when bucket.GetObject(stateFile, options...) returns an error. The existence check passed, so this failure is on the actual download (GetObject) - typically auth on read, SSE-C key mismatch, or object removed between the two calls. %#v prints the raw SDK error.

Solutions

  1. If using SSE-C, ensure the customer key configuration is present in the backend block on every run.
  2. Re-run the operation - a TOCTOU delete by another writer is usually one-off.
  3. Confirm the IAM/RAM principal has `GetObject` (not just `HeadObject`).
  4. Check the SDK error wrapped in %#v for `AccessDenied` or `NoSuchKey` to narrow the cause.
Defensive patterns

Strategy: try-catch

Validate before calling

// if SSE-C is used, ensure the key is configured before reading
if c.serverSideEncryption && c.customerEncryptionKey == nil {
    return fmt.Errorf("state is SSE-C but no customer key provided")
}

Try / catch

output, err := bucket.GetObject(c.stateFile, options...)
if err != nil {
    if isAccessDenied(err) { return fmt.Errorf("missing GetObject permission on %s: %w", c.stateFile, err) }
    if isNoSuchKey(err) { return nil, nil }
    return nil, err
}

Prevention

When it happens

Trigger: bucket.GetObject(c.stateFile) fails after IsObjectExist returned true. Causes: object deleted between check and get (TOCTOU), missing GetObject permission despite Head permission, SSE-C encryption key not supplied or wrong, or a download stream error.

Common situations: Server-side encryption with customer keys configured but key env var unset; another process deleted the state object mid-run; permissions grant HeadObject but not GetObject; bucket policy changed between plan and apply.

Related errors


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

Appendix: source

Thrown at internal/backend/remote-state/oss/client.go:428

	return fmt.Sprintf("%s/%s", c.bucketName, c.stateFile)
}

func (c *RemoteClient) getObj() (*remote.Payload, error) {
	bucket, err := c.ossClient.Bucket(c.bucketName)
	if err != nil {
		return nil, fmt.Errorf("error getting bucket %s: %#v", c.bucketName, err)
	}

	if exist, err := bucket.IsObjectExist(c.stateFile); err != nil {
		return nil, fmt.Errorf("estimating object %s is exist got an error: %#v", c.stateFile, err)
	} else if !exist {
		return nil, nil
	}

	var options []oss.Option
	output, err := bucket.GetObject(c.stateFile, options...)
	if err != nil {
		return nil, fmt.Errorf("error getting object: %#v", err)
	}

	buf := bytes.NewBuffer(nil)
	if _, err := io.Copy(buf, output); err != nil {
		return nil, fmt.Errorf("failed to read remote state: %s", err)
	}
	sum := md5.Sum(buf.Bytes())
	payload := &remote.Payload{
		Data: buf.Bytes(),
		MD5:  sum[:],
	}

	// If there was no data, then return nil
	if len(payload.Data) == 0 {
		return nil, nil
	}

	return payload, nil

View on GitHub (pinned to d32a084675)