hashicorp/terraform · error

unable to read 'content' from response

Error message

unable to read 'content' from response: %w

What it means

Head and Get both succeeded (headers and an open body stream were returned), but io.ReadAll failed while streaming the body. The connection dropped mid-download, the server reset the stream, or the context was cancelled during the read.

Solutions

  1. Retry the Get — the state file did not change, only the stream broke.
  2. Stabilize the network path for large transfers (wired CI runner, less lossy route).
  3. Reduce state size (split into workspaces, prune stale resources) to make transfers more resilient.
  4. Confirm the HTTP client read timeout exceeds the slowest expected download.

Example fix

// before: single shot Get on a flaky link
payload, err := client.Get()
// after: retry read failures specifically
for i := 0; i < 3; i++ {
    payload, err = client.Get()
    if err == nil { break }
    if strings.Contains(err.Error(), "unable to read 'content'") {
        time.Sleep(backoff(i)); continue
    }
    break
}
Defensive patterns

Strategy: retry

Type guard

func isBodyReadErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "unable to read 'content'")
}

Try / catch

// Read-content failures are transient; retry the whole Get
for i := 0; i < 3; i++ {
    payload, err := c.Get()
    if err == nil { return payload, nil }
    if !isBodyReadErr(err) { return nil, err }
    time.Sleep(backoff(i))
}

Prevention

When it happens

Trigger: Server or proxy closed the connection mid-stream; client context cancelled; truncated response; socket read timeout hit during a large state download.

Common situations: Large state files over flaky networks; proxy buffering timeouts; CI runners with aggressive idle-read timeouts; the connection died between receiving headers and the full body.

Related errors


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

Appendix: source

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

		getRequest.OpcSseCustomerKeySha256 = common.String(c.SSECustomerKeySHA256)
		getRequest.OpcSseCustomerAlgorithm = common.String(c.SSECustomerAlgorithm)
	}
	// Get object from OCI
	getResponse, err := c.objectStorageClient.GetObject(ctx, getRequest)
	if err != nil {
		var ociErr common.ServiceError
		if errors.As(err, &ociErr) {
			return nil, fmt.Errorf("failed to access object HttpStatusCode: %d\nOpcRequestId: %s\n message: %s\n ErrorCode: %s", ociErr.GetHTTPStatusCode(), ociErr.GetOpcRequestID(), ociErr.GetMessage(), ociErr.GetCode())

		}
		return nil, fmt.Errorf("failed to access object '%s' in bucket '%s': %w", c.path, c.bucketName, err)
	}
	defer getResponse.Content.Close()

	// Read object content
	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)
	}

View on GitHub (pinned to d32a084675)