hashicorp/terraform · error

Error downloading state

Error message

Error downloading state: %v

What it means

After ReadCurrent returns the state version metadata, the backend downloads the actual state bytes from sv.DownloadURL — a short-lived presigned URL pointing at TFC's object-storage backend (typically S3 for TFC, configurable storage for TFE). A failure here is almost always at the storage/transport layer, not the TFC API.

Solutions

  1. Retry immediately — presigned-URL and transient S3 errors usually clear on the next attempt.
  2. Ensure egress to TFC's state storage endpoints (e.g. *.s3.amazonaws.com for TFC) is allowed by your proxy/firewall.
  3. For TFE, verify the object-storage backend configuration and credentials are healthy.
  4. Check for clock skew on the client (NTP) which can break presigned-URL signatures.
Defensive patterns

Strategy: retry

Validate before calling

// Download within the presigned-URL TTL — call immediately after ReadCurrent
state, err := c.StateVersions.Download(ctx, sv.DownloadURL)
if err != nil { return err }

Try / catch

// Retry S3/storage hiccups with backoff; re-ReadCurrent to refresh URL if expired
err := retry.Backoff(5, func() error {
    fresh, e := c.StateVersions.ReadCurrent(ctx, wsID); if e != nil { return e }
    _, e = c.StateVersions.Download(ctx, fresh.DownloadURL)
    return e
})

Prevention

When it happens

Trigger: StateVersions.Download(ctx, sv.DownloadURL) fails: the presigned URL expired before the request fired, the object-storage endpoint is unreachable, the TLS connection to S3 (or TFE's configured external store) is blocked, the state object was deleted out-of-band, or the response failed checksum/streaming.

Common situations: Long latency between ReadCurrent and Download (URL TTL exceeded); corporate egress proxy blocking the S3 domain; TFE misconfigured object storage; large state timing out; clock skew invalidating the presigned URL signature.

Related errors


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

Appendix: source

Thrown at internal/backend/remote/backend_state.go:67

var _ Fatal = errorUnlockFailed{}

// Get the remote state.
func (r *remoteClient) Get() (*remote.Payload, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics
	ctx := context.Background()

	sv, err := r.client.StateVersions.ReadCurrent(ctx, r.workspace.ID)
	if err != nil {
		if err == tfe.ErrResourceNotFound {
			// If no state exists, then return nil.
			return nil, nil
		}
		return nil, diags.Append(fmt.Errorf("Error retrieving state: %v", err))
	}

	state, err := r.client.StateVersions.Download(ctx, sv.DownloadURL)
	if err != nil {
		return nil, diags.Append(fmt.Errorf("Error downloading state: %v", err))
	}

	// If the state is empty, then return nil.
	if len(state) == 0 {
		return nil, nil
	}

	// Get the MD5 checksum of the state.
	sum := md5.Sum(state)

	return &remote.Payload{
		Data: state,
		MD5:  sum[:],
	}, nil
}

func (r *remoteClient) uploadStateFallback(ctx context.Context, stateFile *statefile.File, state []byte, jsonStateOutputs []byte) error {
	options := tfe.StateVersionCreateOptions{

View on GitHub (pinned to d32a084675)