hashicorp/terraform · error
error downloading state
Error message
error downloading state: %v
What it means
Thrown in getStatePayload after StateVersions.ReadCurrent succeeds but StateVersions.Download fails to fetch the actual state bytes from the DownloadURL. The download uses a separate (often presigned, S3-style) URL distinct from the TFE API endpoint, so it can fail even when the API is healthy. The %v embeds the raw download error.
Solutions
- Retry the terraform command — presigned URL expiry and transient storage errors often resolve on retry
- Verify network egress allows the object storage domain (check the DownloadURL host, not just the TFE API host)
- On self-hosted TFE, confirm the object storage backend is healthy and the TFE instance can reach it
- Check the TFE workspace state version list in the UI to confirm the latest state version is downloadable
Defensive patterns
Strategy: retry
Validate before calling
// Before RefreshState, verify object storage reachability if self-hosted:
if isSelfHostedTFE {
// check the configured storage endpoint is reachable
if err := checkStorageConnectivity(tfEndpoint); err != nil {
return fmt.Errorf("object storage unreachable, state download will fail: %w", err)
}
} Try / catch
// Download failures from presigned URLs are often transient — retry:
for attempt := 0; attempt < 3; attempt++ {
err := stateMgr.RefreshState()
if err == nil || !strings.Contains(err.Error(), "error downloading state") {
return err
}
log.Printf("state download attempt %d failed, retrying", attempt+1)
time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
}
return fmt.Errorf("state download failed after retries") Prevention
- Ensure CI/network egress allows the object storage domain, not just the TFE API domain
- For self-hosted TFE, monitor object storage health as part of platform readiness checks
- Retry RefreshState once or twice before failing a pipeline, as presigned URL expiry is transient
When it happens
Trigger: The presigned download URL expired before the HTTP GET was issued; the object storage backend (S3, Azure blob, GCS) is unavailable; network firewall blocks the object storage domain while allowing the TFE API domain; CORS or TLS issues with the storage endpoint; the state artifact was deleted between ReadCurrent and Download.
Common situations: Self-hosted TFE with misconfigured or temporarily-down object storage; cloud egress firewall rules that allow app.terraform.io but block the underlying S3/Azure bucket domain; slow network where the presigned URL times out before the download starts; TFE backup/restore in progress affecting storage.
Related errors
- Error downloading state
- error retrieving state
- error loading workspace
- error loading workspace
- Error retrieving state
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/2e83cebad7c04037.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/state.go:422
func (s *State) getStatePayload() (*remote.Payload, error) {
ctx := context.Background()
// Check the x-terraform-snapshot-interval header to see if it has a non-empty
// value which would indicate snapshots are enabled
ctx = tfe.ContextWithResponseHeaderHook(ctx, s.readSnapshotIntervalHeader)
sv, err := s.tfeClient.StateVersions.ReadCurrent(ctx, s.workspace.ID)
if err != nil {
if err == tfe.ErrResourceNotFound {
// If no state exists, then return nil.
return nil, nil
}
return nil, fmt.Errorf("error retrieving state: %v", err)
}
state, err := s.tfeClient.StateVersions.Download(ctx, sv.DownloadURL)
if err != nil {
return nil, 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
}
type errorUnlockFailed struct {
innerError errorView on GitHub (pinned to d32a084675)